mirror of https://github.com/axmolengine/axmol.git
Added new audio engine with wav support
This commit is contained in:
parent
e99b99cc79
commit
2db5a89b87
|
@ -236,6 +236,9 @@
|
|||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\include\Export.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\include\SimpleAudioEngine.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\Audio.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioCachePlayer.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioEngine-winrt.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioSourceReader.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\MediaStreamer.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\base\allocator\CCAllocatorBase.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\base\allocator\CCAllocatorDiagnostics.h" />
|
||||
|
@ -830,11 +833,15 @@
|
|||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\3d\CCSprite3DMaterial.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\3d\CCTerrain.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\3d\CCTextureCube.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\AudioEngine.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\Audio.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<ForcedIncludeFiles>
|
||||
</ForcedIncludeFiles>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioCachePlayer.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioEngine-winrt.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioSourceReader.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\MediaStreamer.cpp">
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<ForcedIncludeFiles>
|
||||
|
|
|
@ -718,9 +718,6 @@
|
|||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\editor-support\cocostudio\TriggerObj.h">
|
||||
<Filter>cocostudio\trigger</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\include\AudioEngine.h">
|
||||
<Filter>cocosdenshion\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\include\Export.h">
|
||||
<Filter>cocosdenshion\Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
@ -1791,6 +1788,18 @@
|
|||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\renderer\CCVertexAttribBinding.h">
|
||||
<Filter>renderer</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\include\AudioEngine.h">
|
||||
<Filter>audioengine</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioCachePlayer.h">
|
||||
<Filter>audioengine</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioEngine-winrt.h">
|
||||
<Filter>audioengine</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioSourceReader.h">
|
||||
<Filter>audioengine</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\cocos2d.cpp" />
|
||||
|
@ -3405,6 +3414,18 @@
|
|||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\renderer\CCVertexAttribBinding.cpp">
|
||||
<Filter>renderer</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\AudioEngine.cpp">
|
||||
<Filter>audioengine</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioCachePlayer.cpp">
|
||||
<Filter>audioengine</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioEngine-winrt.cpp">
|
||||
<Filter>audioengine</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\..\audio\winrt\AudioSourceReader.cpp">
|
||||
<Filter>audioengine</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="2d">
|
||||
|
@ -3662,6 +3683,9 @@
|
|||
<Filter Include="external\poly2tri\sweep">
|
||||
<UniqueIdentifier>{932c5f6e-07b3-4b34-97ae-2f3d42024149}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="audioengine">
|
||||
<UniqueIdentifier>{bd1a6214-d6e0-4341-a1fc-79da99b55e35}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)..\..\..\cocos2d.def" />
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
||||
#include "audio/include/AudioEngine.h"
|
||||
#include "platform/CCFileUtils.h"
|
||||
|
@ -36,6 +36,8 @@
|
|||
#include "apple/AudioEngine-inl.h"
|
||||
#elif CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
#include "win32/AudioEngine-win32.h"
|
||||
#elif CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
#include "winrt/AudioEngine-winrt.h"
|
||||
#endif
|
||||
|
||||
#define TIME_DELAY_PRECISION 0.0001
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
||||
#ifndef __AUDIO_ENGINE_H_
|
||||
#define __AUDIO_ENGINE_H_
|
||||
|
|
|
@ -0,0 +1,568 @@
|
|||
/*
|
||||
* cocos2d-x http://www.cocos2d-x.org
|
||||
*
|
||||
* Copyright (c) 2010-2011 - cocos2d-x community
|
||||
*
|
||||
* Portions Copyright (c) Microsoft Open Technologies, Inc.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
|
||||
#include "AudioCachePlayer.h"
|
||||
|
||||
using namespace cocos2d;
|
||||
using namespace cocos2d::experimental;
|
||||
|
||||
inline void ThrowIfFailed(HRESULT hr)
|
||||
{
|
||||
if (FAILED(hr)) {
|
||||
// Set a breakpoint on this line to catch XAudio2 API errors.
|
||||
throw Platform::Exception::CreateException(hr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// AudioCache
|
||||
AudioCache::AudioCache()
|
||||
: _isReady(false)
|
||||
, _fileFullPath("")
|
||||
, _srcReader(nullptr)
|
||||
, _fileFormat(FileFormat::UNKNOWN)
|
||||
{
|
||||
_callbacks.clear();
|
||||
memset(&_audInfo, 0, sizeof(AudioInfo));
|
||||
}
|
||||
|
||||
AudioCache::AudioCache(const AudioCache& obj)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
AudioCache& AudioCache::operator=(const AudioCache& rhs)
|
||||
{
|
||||
// TODO
|
||||
return *this;
|
||||
}
|
||||
|
||||
AudioCache::~AudioCache()
|
||||
{
|
||||
_callbacks.clear();
|
||||
|
||||
if (nullptr != _srcReader) {
|
||||
delete _srcReader;
|
||||
_srcReader = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioCache::readDataTask()
|
||||
{
|
||||
std::wstring path(_fileFullPath.begin(), _fileFullPath.end());
|
||||
|
||||
if (nullptr != _srcReader) {
|
||||
delete _srcReader;
|
||||
_srcReader = nullptr;
|
||||
}
|
||||
|
||||
switch (_fileFormat)
|
||||
{
|
||||
case FileFormat::WAV:
|
||||
_srcReader = new (std::nothrow) WAVReader();
|
||||
if (_srcReader->initialize(_fileFullPath)) {
|
||||
_audInfo._totalAudioBytes = _srcReader->getTotalAudioBytes();
|
||||
_audInfo._wfx = _srcReader->getWaveFormatInfo();
|
||||
_isReady = true;
|
||||
invokeCallbacks();
|
||||
}
|
||||
break;
|
||||
|
||||
case FileFormat::OGG:
|
||||
break;
|
||||
|
||||
case FileFormat::MP3:
|
||||
break;
|
||||
|
||||
case FileFormat::UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioCache::addCallback(const std::function<void()> &callback)
|
||||
{
|
||||
_cbMutex.lock();
|
||||
if (_isReady) {
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
_callbacks.push_back(callback);
|
||||
}
|
||||
_cbMutex.unlock();
|
||||
}
|
||||
|
||||
void AudioCache::invokeCallbacks()
|
||||
{
|
||||
_cbMutex.lock();
|
||||
auto cnt = _callbacks.size();
|
||||
for (size_t ind = 0; ind < cnt; ind++)
|
||||
{
|
||||
_callbacks[ind]();
|
||||
}
|
||||
_callbacks.clear();
|
||||
_cbMutex.unlock();
|
||||
}
|
||||
|
||||
bool AudioCache::getChunk(AudioDataChunk& chunk)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (nullptr != _srcReader) {
|
||||
ret = _srcReader->consumeChunk(chunk);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AudioCache::doBuffering()
|
||||
{
|
||||
if (isStreamingSource()){
|
||||
_srcReader->produceChunk();
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioCache::isStreamingSource()
|
||||
{
|
||||
if (nullptr != _srcReader) {
|
||||
return _srcReader->isStreamingSource();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioCache::seek(const float ratio)
|
||||
{
|
||||
if (nullptr != _srcReader){
|
||||
_srcReader->seekTo(ratio);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// AudioPlayer
|
||||
AudioPlayer::AudioPlayer()
|
||||
: _loop(false)
|
||||
, _ready(false)
|
||||
, _current(0.0)
|
||||
, _volume(0.0)
|
||||
, _duration(0.0)
|
||||
, _cache(nullptr)
|
||||
, _totalSamples(0)
|
||||
, _samplesOffset(0)
|
||||
, _criticalError(false)
|
||||
, _isStreaming(false)
|
||||
, _finishCallback(nullptr)
|
||||
, _xaMasterVoice(nullptr)
|
||||
, _xaSourceVoice(nullptr)
|
||||
, _state(AudioPlayerState::INITIALZING)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
AudioPlayer::~AudioPlayer()
|
||||
{
|
||||
free();
|
||||
}
|
||||
|
||||
void AudioPlayer::stop()
|
||||
{
|
||||
_stop();
|
||||
}
|
||||
|
||||
void AudioPlayer::pause()
|
||||
{
|
||||
_stop(true);
|
||||
}
|
||||
|
||||
bool AudioPlayer::update()
|
||||
{
|
||||
if (_criticalError){
|
||||
free();
|
||||
init();
|
||||
}
|
||||
|
||||
if (_cache != nullptr) {
|
||||
_cache->doBuffering();
|
||||
}
|
||||
|
||||
//log("bufferQueued: %d, _current: %f", _cachedBufferQ.size(), _current);
|
||||
return _criticalError;
|
||||
}
|
||||
|
||||
void AudioPlayer::resume()
|
||||
{
|
||||
_play(true);
|
||||
}
|
||||
|
||||
bool AudioPlayer::isInError()
|
||||
{
|
||||
return _criticalError;
|
||||
}
|
||||
|
||||
float AudioPlayer::getDuration()
|
||||
{
|
||||
if (nullptr == _cache) {
|
||||
return _duration;
|
||||
}
|
||||
|
||||
auto fmt = _cache->_audInfo._wfx;
|
||||
|
||||
if (!fmt.nChannels) {
|
||||
return _duration;
|
||||
}
|
||||
|
||||
if ((int)_duration <= 0)
|
||||
{
|
||||
switch (fmt.wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
_duration = (float(_cache->_audInfo._totalAudioBytes / ((fmt.wBitsPerSample / 8) * fmt.nChannels)) / fmt.nSamplesPerSec) * 1000;
|
||||
_totalSamples = fmt.nSamplesPerSec * _duration / 1000;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _duration;
|
||||
}
|
||||
|
||||
float AudioPlayer::getCurrentTime()
|
||||
{
|
||||
_stMutex.lock();
|
||||
auto samplesPlayed = getSourceVoiceState().SamplesPlayed;
|
||||
//log("_samplesOffset: %lu, samplesPlayed: %lu, _current: %f", (UINT32)_samplesOffset, (UINT32)samplesPlayed, _current);
|
||||
_current += ((samplesPlayed - _samplesOffset) / (float)_totalSamples) * _duration;
|
||||
_current = _current > _duration ? 0.0 : _current;
|
||||
_samplesOffset = samplesPlayed;
|
||||
_stMutex.unlock();
|
||||
return _current;
|
||||
}
|
||||
|
||||
bool AudioPlayer::setTime(float time)
|
||||
{
|
||||
bool ret = true;
|
||||
_stop();
|
||||
|
||||
if (!_isStreaming) {
|
||||
auto fmt = _cache->_audInfo._wfx;
|
||||
int seek = (time / _duration) * _totalSamples;
|
||||
seek -= (seek % (fmt.nChannels * fmt.nBlockAlign));
|
||||
|
||||
_xaBuffer.LoopCount = 0;
|
||||
_xaBuffer.PlayBegin = seek;
|
||||
_xaBuffer.PlayLength = _totalSamples - seek;
|
||||
|
||||
if (_xaBuffer.PlayBegin >= _totalSamples) {
|
||||
_xaBuffer.PlayBegin = _totalSamples - (fmt.nChannels * fmt.nBlockAlign);
|
||||
_xaBuffer.PlayLength = (fmt.nChannels * fmt.nBlockAlign);
|
||||
}
|
||||
}
|
||||
|
||||
_stMutex.lock();
|
||||
_samplesOffset = getSourceVoiceState().SamplesPlayed;
|
||||
_current = time;
|
||||
_stMutex.unlock();
|
||||
_play();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AudioPlayer::setVolume(float volume)
|
||||
{
|
||||
if (_xaMasterVoice != nullptr){
|
||||
if (FAILED(_xaMasterVoice->SetVolume(volume))) {
|
||||
error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioPlayer::play2d(AudioCache* cache)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (cache != nullptr)
|
||||
{
|
||||
_cache = cache;
|
||||
if (nullptr == _xaSourceVoice && _ready) {
|
||||
|
||||
XAUDIO2_SEND_DESCRIPTOR descriptors[1];
|
||||
descriptors[0].pOutputVoice = _xaMasterVoice;
|
||||
descriptors[0].Flags = 0;
|
||||
XAUDIO2_VOICE_SENDS sends = { 0 };
|
||||
sends.SendCount = 1;
|
||||
sends.pSends = descriptors;
|
||||
ThrowIfFailed(_xaEngine->CreateSourceVoice(&_xaSourceVoice, &cache->_audInfo._wfx, 0, 1.0, this, &sends));
|
||||
}
|
||||
|
||||
_isStreaming = _cache->isStreamingSource();
|
||||
_duration = getDuration();
|
||||
ret = _play();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AudioPlayer::init()
|
||||
{
|
||||
memset(&_xaBuffer, 0, sizeof(_xaBuffer));
|
||||
ThrowIfFailed(XAudio2Create(_xaEngine.ReleaseAndGetAddressOf()));
|
||||
|
||||
#if defined(_DEBUG)
|
||||
XAUDIO2_DEBUG_CONFIGURATION debugConfig = { 0 };
|
||||
debugConfig.BreakMask = XAUDIO2_LOG_ERRORS;
|
||||
debugConfig.TraceMask = XAUDIO2_LOG_ERRORS;
|
||||
_xaEngine->SetDebugConfiguration(&debugConfig);
|
||||
#endif
|
||||
|
||||
_xaEngine->RegisterForCallbacks(this);
|
||||
ThrowIfFailed(_xaEngine->CreateMasteringVoice(&_xaMasterVoice, XAUDIO2_DEFAULT_CHANNELS, XAUDIO2_DEFAULT_SAMPLERATE, 0, nullptr, nullptr, AudioCategory_GameMedia));
|
||||
_ready = true;
|
||||
_state = AudioPlayerState::READY;
|
||||
}
|
||||
|
||||
void AudioPlayer::free()
|
||||
{
|
||||
_stop();
|
||||
memset(&_xaBuffer, 0, sizeof(_xaBuffer));
|
||||
|
||||
if (_xaEngine) {
|
||||
_xaEngine->StopEngine();
|
||||
}
|
||||
|
||||
if (_xaSourceVoice != nullptr) {
|
||||
_xaSourceVoice->DestroyVoice();
|
||||
_xaSourceVoice = nullptr;
|
||||
}
|
||||
|
||||
if (_xaMasterVoice != nullptr) {
|
||||
_xaMasterVoice->DestroyVoice();
|
||||
_xaMasterVoice = nullptr;
|
||||
}
|
||||
|
||||
while (!_cachedBufferQ.empty()) {
|
||||
popBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioPlayer::_play(bool resume)
|
||||
{
|
||||
do {
|
||||
if (!resume) {
|
||||
_cache->seek(_current / _duration);
|
||||
submitBuffers();
|
||||
}
|
||||
|
||||
if (_state == AudioPlayerState::PAUSED && !resume || nullptr == _xaSourceVoice) break;
|
||||
|
||||
if (FAILED(_xaSourceVoice->Start())) {
|
||||
error();
|
||||
}
|
||||
else {
|
||||
_state = AudioPlayerState::PLAYING;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
return !_criticalError;
|
||||
}
|
||||
|
||||
void AudioPlayer::_stop(bool pause)
|
||||
{
|
||||
if (_xaSourceVoice != nullptr) {
|
||||
|
||||
if (FAILED(_xaSourceVoice->Stop())) {
|
||||
error();
|
||||
}
|
||||
else {
|
||||
if (!pause) {
|
||||
_xaSourceVoice->FlushSourceBuffers();
|
||||
if (_state != AudioPlayerState::PAUSED) _state = AudioPlayerState::STOPPED;
|
||||
}
|
||||
else {
|
||||
_state = AudioPlayerState::PAUSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::error()
|
||||
{
|
||||
_criticalError = true;
|
||||
_ready = false;
|
||||
_state = AudioPlayerState::ERRORED;
|
||||
}
|
||||
|
||||
void AudioPlayer::popBuffer()
|
||||
{
|
||||
_bqMutex.lock();
|
||||
if (!_cachedBufferQ.empty()) {
|
||||
delete[] _cachedBufferQ.front()._data;
|
||||
_cachedBufferQ.pop();
|
||||
}
|
||||
_bqMutex.unlock();
|
||||
}
|
||||
|
||||
bool AudioPlayer::submitBuffers()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
_bqMutex.lock();
|
||||
do {
|
||||
if (nullptr == _xaSourceVoice) break;
|
||||
if (!_cachedBufferQ.size() || (_isStreaming && _cachedBufferQ.size() < QUEUEBUFFER_NUM)) {
|
||||
AudioDataChunk chunk;
|
||||
if (_cache->getChunk(chunk)) {
|
||||
_xaBuffer.AudioBytes = chunk._dataSize;
|
||||
_xaBuffer.pAudioData = chunk._data;
|
||||
_xaBuffer.Flags = chunk._endOfStream ? XAUDIO2_END_OF_STREAM : 0;
|
||||
_cachedBufferQ.push(chunk);
|
||||
ret = SUCCEEDED(_xaSourceVoice->SubmitSourceBuffer(&_xaBuffer));
|
||||
if (!_isStreaming) break;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!_isStreaming) {
|
||||
ret = SUCCEEDED(_xaSourceVoice->SubmitSourceBuffer(&_xaBuffer));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
} while (ret);
|
||||
_bqMutex.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AudioPlayer::updateState()
|
||||
{
|
||||
if (!_isStreaming) {
|
||||
_stMutex.lock();
|
||||
_samplesOffset = getSourceVoiceState().SamplesPlayed;
|
||||
_stMutex.unlock();
|
||||
}
|
||||
else {
|
||||
if (_cachedBufferQ.size() > getSourceVoiceState(true).BuffersQueued) {
|
||||
popBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::onBufferRunOut()
|
||||
{
|
||||
_stMutex.lock();
|
||||
_samplesOffset = 0;
|
||||
_current = 0.0;
|
||||
_xaBuffer.PlayBegin = _xaBuffer.PlayLength = 0;
|
||||
_stMutex.unlock();
|
||||
|
||||
if (!_loop) {
|
||||
_stop();
|
||||
//invokeFinishCallback();
|
||||
}
|
||||
else {
|
||||
_play();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::invokeFinishCallback()
|
||||
{
|
||||
if (_finishCallback) {
|
||||
_finishCallback(0, "");
|
||||
}
|
||||
}
|
||||
|
||||
XAUDIO2_VOICE_STATE AudioPlayer::getSourceVoiceState(bool fast)
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
memset(&state, 0, sizeof(XAUDIO2_VOICE_STATE));
|
||||
|
||||
if (_xaSourceVoice != nullptr) {
|
||||
_xaSourceVoice->GetState(&state, fast ? XAUDIO2_VOICE_NOSAMPLESPLAYED : 0);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
// IXAudio2EngineCallback
|
||||
void AudioPlayer::OnProcessingPassStart()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioPlayer::OnProcessingPassEnd()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioPlayer::OnCriticalError(HRESULT err)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(err);
|
||||
error();
|
||||
}
|
||||
|
||||
// IXAudio2VoiceCallback
|
||||
void AudioPlayer::OnVoiceProcessingPassStart(UINT32 uBytesRequired)
|
||||
{
|
||||
if (uBytesRequired && _isStreaming){
|
||||
submitBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::OnVoiceProcessingPassEnd()
|
||||
{
|
||||
}
|
||||
|
||||
void AudioPlayer::OnStreamEnd()
|
||||
{
|
||||
onBufferRunOut();
|
||||
}
|
||||
|
||||
void AudioPlayer::OnBufferStart(void* pBufferContext)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(pBufferContext);
|
||||
}
|
||||
|
||||
void AudioPlayer::OnBufferEnd(void* pBufferContext)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(pBufferContext);
|
||||
updateState();
|
||||
}
|
||||
|
||||
void AudioPlayer::OnLoopEnd(void* pBufferContext)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(pBufferContext);
|
||||
|
||||
if (!_loop) {
|
||||
_stop();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::OnVoiceError(void* pBufferContext, HRESULT err)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(pBufferContext);
|
||||
UNREFERENCED_PARAMETER(err);
|
||||
error();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* cocos2d-x http://www.cocos2d-x.org
|
||||
*
|
||||
* Copyright (c) 2010-2011 - cocos2d-x community
|
||||
*
|
||||
* Portions Copyright (c) Microsoft Open Technologies, Inc.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
|
||||
#ifndef __AUDIO_CACHE_PLAYER_H_
|
||||
#define __AUDIO_CACHE_PLAYER_H_
|
||||
|
||||
#include "AudioSourceReader.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
namespace experimental{
|
||||
|
||||
typedef struct AudioInfo
|
||||
{
|
||||
size_t _totalAudioBytes;
|
||||
WAVEFORMATEX _wfx;
|
||||
} AudioInfo;
|
||||
|
||||
enum class AudioPlayerState
|
||||
{
|
||||
ERRORED = -1,
|
||||
INITIALZING,
|
||||
READY,
|
||||
PLAYING,
|
||||
PAUSED,
|
||||
STOPPED
|
||||
};
|
||||
|
||||
class AudioCache
|
||||
{
|
||||
public:
|
||||
AudioCache();
|
||||
~AudioCache();
|
||||
|
||||
void readDataTask();
|
||||
void addCallback(const std::function<void()> &callback);
|
||||
bool getChunk(AudioDataChunk& chunk);
|
||||
void doBuffering();
|
||||
bool isStreamingSource();
|
||||
void seek(const float ratio);
|
||||
|
||||
protected:
|
||||
AudioCache(const AudioCache&);
|
||||
AudioCache& operator=(const AudioCache&);
|
||||
void invokeCallbacks();
|
||||
|
||||
private:
|
||||
bool _isReady;
|
||||
AudioInfo _audInfo;
|
||||
std::mutex _cbMutex;
|
||||
FileFormat _fileFormat;
|
||||
std::string _fileFullPath;
|
||||
AudioSourceReader *_srcReader;
|
||||
std::vector<std::function<void()>> _callbacks;
|
||||
|
||||
|
||||
friend class AudioPlayer;
|
||||
friend class AudioEngineImpl;
|
||||
};
|
||||
|
||||
|
||||
class AudioPlayer : public IXAudio2EngineCallback, IXAudio2VoiceCallback
|
||||
{
|
||||
public:
|
||||
AudioPlayer();
|
||||
virtual ~AudioPlayer();
|
||||
|
||||
void stop();
|
||||
void pause();
|
||||
bool update();
|
||||
void resume();
|
||||
bool isInError();
|
||||
float getDuration();
|
||||
float getCurrentTime();
|
||||
bool setTime(float time);
|
||||
void setVolume(float volume);
|
||||
bool play2d(AudioCache* cache);
|
||||
AudioPlayerState getState() { return _state; }
|
||||
|
||||
protected:
|
||||
AudioPlayer(AudioPlayer&);
|
||||
AudioPlayer& operator=(AudioPlayer&);
|
||||
|
||||
void init();
|
||||
void free();
|
||||
void error();
|
||||
void popBuffer();
|
||||
void updateState();
|
||||
bool submitBuffers();
|
||||
void onBufferRunOut();
|
||||
void invokeFinishCallback();
|
||||
void _stop(bool pause = false);
|
||||
bool _play(bool resume = false);
|
||||
XAUDIO2_VOICE_STATE getSourceVoiceState(bool fast = false);
|
||||
|
||||
// IXAudio2EngineCallback
|
||||
STDMETHOD_(void, OnProcessingPassStart) () override;
|
||||
STDMETHOD_(void, OnProcessingPassEnd) () override;
|
||||
STDMETHOD_(void, OnCriticalError) (HRESULT error) override;
|
||||
|
||||
// IXAudio2VoiceCallback
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32 uBytesRequired) override;
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () override;
|
||||
STDMETHOD_(void, OnStreamEnd) () override;
|
||||
STDMETHOD_(void, OnBufferStart) (void* pBufferContext) override;
|
||||
STDMETHOD_(void, OnBufferEnd) (void* pBufferContext) override;
|
||||
STDMETHOD_(void, OnLoopEnd) (void* pBufferContext) override;
|
||||
STDMETHOD_(void, OnVoiceError) (void *pBufferContext, HRESULT error) override;
|
||||
|
||||
private:
|
||||
bool _loop;
|
||||
bool _ready;
|
||||
float _volume;
|
||||
float _current;
|
||||
float _duration;
|
||||
bool _criticalError;
|
||||
bool _isStreaming;
|
||||
UINT64 _totalSamples;
|
||||
UINT64 _samplesOffset;
|
||||
|
||||
XAUDIO2_BUFFER _xaBuffer;
|
||||
IXAudio2SourceVoice *_xaSourceVoice;
|
||||
IXAudio2MasteringVoice *_xaMasterVoice;
|
||||
Microsoft::WRL::ComPtr<IXAudio2> _xaEngine;
|
||||
|
||||
AudioCache *_cache;
|
||||
std::mutex _stMutex;
|
||||
std::mutex _bqMutex;
|
||||
AudioPlayerState _state;
|
||||
std::queue<AudioDataChunk> _cachedBufferQ;
|
||||
std::function<void(int, const std::string &)> _finishCallback;
|
||||
|
||||
|
||||
friend class AudioEngineImpl;
|
||||
};
|
||||
|
||||
}
|
||||
NS_CC_END
|
||||
#endif // __AUDIO_CACHE_PLAYER_H_
|
||||
#endif
|
||||
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* cocos2d-x http://www.cocos2d-x.org
|
||||
*
|
||||
* Copyright (c) 2010-2011 - cocos2d-x community
|
||||
*
|
||||
* Portions Copyright (c) Microsoft Open Technologies, Inc.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
|
||||
#include "AudioEngine-winrt.h"
|
||||
#include <condition_variable>
|
||||
|
||||
|
||||
using namespace cocos2d;
|
||||
using namespace cocos2d::experimental;
|
||||
|
||||
|
||||
namespace cocos2d {
|
||||
namespace experimental {
|
||||
class AudioEngineThreadPool
|
||||
{
|
||||
public:
|
||||
AudioEngineThreadPool()
|
||||
: _running(true)
|
||||
, _numThread(6)
|
||||
{
|
||||
_threads.reserve(_numThread);
|
||||
_tasks.reserve(_numThread);
|
||||
|
||||
for (int index = 0; index < _numThread; ++index) {
|
||||
_tasks.push_back(nullptr);
|
||||
_threads.push_back(std::thread(std::bind(&AudioEngineThreadPool::threadFunc, this, index)));
|
||||
}
|
||||
}
|
||||
|
||||
void addTask(const std::function<void()> &task){
|
||||
_taskMutex.lock();
|
||||
int targetIndex = -1;
|
||||
for (int index = 0; index < _numThread; ++index) {
|
||||
if (_tasks[index] == nullptr) {
|
||||
targetIndex = index;
|
||||
_tasks[index] = task;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (targetIndex == -1) {
|
||||
_tasks.push_back(task);
|
||||
_threads.push_back(std::thread(std::bind(&AudioEngineThreadPool::threadFunc, this, _numThread)));
|
||||
|
||||
_numThread++;
|
||||
}
|
||||
_taskMutex.unlock();
|
||||
_sleepCondition.notify_all();
|
||||
}
|
||||
|
||||
void destroy()
|
||||
{
|
||||
_running = false;
|
||||
_sleepCondition.notify_all();
|
||||
|
||||
for (int index = 0; index < _numThread; ++index) {
|
||||
_threads[index].join();
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool _running;
|
||||
std::vector<std::thread> _threads;
|
||||
std::vector< std::function<void()> > _tasks;
|
||||
|
||||
void threadFunc(int index)
|
||||
{
|
||||
while (_running) {
|
||||
std::function<void()> task = nullptr;
|
||||
_taskMutex.lock();
|
||||
task = _tasks[index];
|
||||
_taskMutex.unlock();
|
||||
|
||||
if (nullptr == task)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(_sleepMutex);
|
||||
_sleepCondition.wait(lk);
|
||||
continue;
|
||||
}
|
||||
|
||||
task();
|
||||
|
||||
_taskMutex.lock();
|
||||
_tasks[index] = nullptr;
|
||||
_taskMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
int _numThread;
|
||||
|
||||
std::mutex _taskMutex;
|
||||
std::mutex _sleepMutex;
|
||||
std::condition_variable _sleepCondition;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
AudioEngineImpl::AudioEngineImpl()
|
||||
: _lazyInitLoop(true)
|
||||
, _currentAudioID(0)
|
||||
, _threadPool(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AudioEngineImpl::~AudioEngineImpl()
|
||||
{
|
||||
_audioCaches.clear();
|
||||
|
||||
if (_threadPool) {
|
||||
_threadPool->destroy();
|
||||
delete _threadPool;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioEngineImpl::init()
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (nullptr == _threadPool) {
|
||||
_threadPool = new (std::nothrow) AudioEngineThreadPool();
|
||||
}
|
||||
|
||||
ret = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int AudioEngineImpl::play2d(const std::string &filePath, bool loop, float volume)
|
||||
{
|
||||
AudioCache* audioCache = nullptr;
|
||||
auto it = _audioCaches.find(filePath);
|
||||
if (it == _audioCaches.end()) {
|
||||
audioCache = &_audioCaches[filePath];
|
||||
|
||||
auto ext = filePath.substr(filePath.rfind('.'));
|
||||
transform(ext.begin(), ext.end(), ext.begin(), tolower);
|
||||
|
||||
bool eraseCache = true;
|
||||
|
||||
if (ext.compare(".wav") == 0){
|
||||
audioCache->_fileFormat = FileFormat::WAV;
|
||||
eraseCache = false;
|
||||
}
|
||||
else if (ext.compare(".ogg") == 0){
|
||||
audioCache->_fileFormat = FileFormat::OGG;
|
||||
//eraseCache = false; //TODO add support for OGG
|
||||
}
|
||||
else if (ext.compare(".mp3") == 0){
|
||||
audioCache->_fileFormat = FileFormat::MP3;
|
||||
//eraseCache = false; //TODO add support for MP3
|
||||
}
|
||||
else{
|
||||
log("unsupported media type:%s\n", ext.c_str());
|
||||
eraseCache = false;
|
||||
}
|
||||
|
||||
if (eraseCache){
|
||||
_audioCaches.erase(filePath);
|
||||
return AudioEngine::INVALID_AUDIO_ID;
|
||||
}
|
||||
|
||||
std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
|
||||
audioCache->_fileFullPath = fullPath;
|
||||
_threadPool->addTask(std::bind(&AudioCache::readDataTask, audioCache));
|
||||
}
|
||||
else {
|
||||
audioCache = &it->second;
|
||||
}
|
||||
|
||||
auto player = &_audioPlayers[_currentAudioID];
|
||||
player->_loop = loop;
|
||||
player->_volume = volume;
|
||||
audioCache->addCallback(std::bind(&AudioEngineImpl::_play2d, this, audioCache, _currentAudioID));
|
||||
|
||||
if (_lazyInitLoop) {
|
||||
_lazyInitLoop = false;
|
||||
|
||||
auto scheduler = cocos2d::Director::getInstance()->getScheduler();
|
||||
scheduler->schedule(schedule_selector(AudioEngineImpl::update), this, 0.05f, false);
|
||||
}
|
||||
|
||||
return _currentAudioID++;
|
||||
}
|
||||
|
||||
void AudioEngineImpl::_play2d(AudioCache *cache, int audioID)
|
||||
{
|
||||
if (cache->_isReady){
|
||||
auto playerIt = _audioPlayers.find(audioID);
|
||||
if (playerIt != _audioPlayers.end()) {
|
||||
if (playerIt->second.play2d(cache)) {
|
||||
AudioEngine::_audioIDInfoMap[audioID].state = AudioEngine::AudioState::PLAYING;
|
||||
}
|
||||
else{
|
||||
_threadMutex.lock();
|
||||
_toRemoveAudioIDs.push_back(audioID);
|
||||
_threadMutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
_threadMutex.lock();
|
||||
_toRemoveCaches.push_back(cache);
|
||||
_toRemoveAudioIDs.push_back(audioID);
|
||||
_threadMutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEngineImpl::setVolume(int audioID, float volume)
|
||||
{
|
||||
auto& player = _audioPlayers[audioID];
|
||||
|
||||
if (player._ready){
|
||||
player.setVolume(volume);
|
||||
}
|
||||
|
||||
if (player.isInError()) {
|
||||
log("%s: audio id = %d, error.\n", __FUNCTION__, audioID);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEngineImpl::setLoop(int audioID, bool loop)
|
||||
{
|
||||
auto& player = _audioPlayers[audioID];
|
||||
|
||||
if (player._ready) {
|
||||
player._loop = loop;
|
||||
}
|
||||
|
||||
if (player.isInError()) {
|
||||
log("%s: audio id = %d, error.\n", __FUNCTION__, audioID);
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioEngineImpl::pause(int audioID)
|
||||
{
|
||||
bool ret = false;
|
||||
auto& player = _audioPlayers[audioID];
|
||||
|
||||
if (player._ready) {
|
||||
player.pause();
|
||||
AudioEngine::_audioIDInfoMap[audioID].state = AudioEngine::AudioState::PAUSED;
|
||||
}
|
||||
|
||||
ret = !player.isInError();
|
||||
|
||||
if (!ret) {
|
||||
log("%s: audio id = %d, error.\n", __FUNCTION__, audioID);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AudioEngineImpl::resume(int audioID)
|
||||
{
|
||||
bool ret = false;
|
||||
auto& player = _audioPlayers[audioID];
|
||||
|
||||
if (player._ready) {
|
||||
player.resume();
|
||||
AudioEngine::_audioIDInfoMap[audioID].state = AudioEngine::AudioState::PLAYING;
|
||||
}
|
||||
|
||||
ret = !player.isInError();
|
||||
|
||||
if (!ret) {
|
||||
log("%s: audio id = %d, error.\n", __FUNCTION__, audioID);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AudioEngineImpl::stop(int audioID)
|
||||
{
|
||||
bool ret = false;
|
||||
auto& player = _audioPlayers[audioID];
|
||||
|
||||
if (player._ready) {
|
||||
player.stop();
|
||||
ret = !player.isInError();
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
log("%s: audio id = %d, error.\n", __FUNCTION__, audioID);
|
||||
}
|
||||
|
||||
_audioPlayers.erase(audioID);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AudioEngineImpl::stopAll()
|
||||
{
|
||||
for (auto &player : _audioPlayers) {
|
||||
player.second.stop();
|
||||
}
|
||||
|
||||
_audioPlayers.clear();
|
||||
}
|
||||
|
||||
float AudioEngineImpl::getDuration(int audioID)
|
||||
{
|
||||
auto& player = _audioPlayers[audioID];
|
||||
|
||||
if (player._ready) {
|
||||
return player.getDuration();
|
||||
}
|
||||
else {
|
||||
return AudioEngine::TIME_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
float AudioEngineImpl::getCurrentTime(int audioID)
|
||||
{
|
||||
float ret = 0.0f;
|
||||
auto& player = _audioPlayers[audioID];
|
||||
|
||||
if (player._ready) {
|
||||
ret = player.getCurrentTime();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool AudioEngineImpl::setCurrentTime(int audioID, float time)
|
||||
{
|
||||
bool ret = false;
|
||||
auto& player = _audioPlayers[audioID];
|
||||
|
||||
if (player._ready) {
|
||||
ret = player.setTime(time);
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
log("%s: audio id = %d, error.\n", __FUNCTION__, audioID);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void AudioEngineImpl::setFinishCallback(int audioID, const std::function<void(int, const std::string &)> &callback)
|
||||
{
|
||||
_audioPlayers[audioID]._finishCallback = callback;
|
||||
}
|
||||
|
||||
void AudioEngineImpl::update(float dt)
|
||||
{
|
||||
int audioID;
|
||||
|
||||
if (_threadMutex.try_lock()) {
|
||||
size_t removeAudioCount = _toRemoveAudioIDs.size();
|
||||
for (size_t index = 0; index < removeAudioCount; ++index) {
|
||||
audioID = _toRemoveAudioIDs[index];
|
||||
auto playerIt = _audioPlayers.find(audioID);
|
||||
if (playerIt != _audioPlayers.end()) {
|
||||
if (playerIt->second._finishCallback) {
|
||||
auto& audioInfo = AudioEngine::_audioIDInfoMap[audioID];
|
||||
playerIt->second._finishCallback(audioID, *audioInfo.filePath);
|
||||
}
|
||||
_audioPlayers.erase(audioID);
|
||||
AudioEngine::remove(audioID);
|
||||
}
|
||||
}
|
||||
|
||||
size_t removeCacheCount = _toRemoveCaches.size();
|
||||
for (size_t index = 0; index < removeCacheCount; ++index) {
|
||||
auto itEnd = _audioCaches.end();
|
||||
for (auto it = _audioCaches.begin(); it != itEnd; ++it) {
|
||||
if (&it->second == _toRemoveCaches[index]) {
|
||||
_audioCaches.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_threadMutex.unlock();
|
||||
}
|
||||
|
||||
for (auto it = _audioPlayers.begin(); it != _audioPlayers.end();) {
|
||||
audioID = it->first;
|
||||
auto& player = it->second;
|
||||
|
||||
if (player._ready && player._state == AudioPlayerState::STOPPED) {
|
||||
if (player._finishCallback) {
|
||||
auto& audioInfo = AudioEngine::_audioIDInfoMap[audioID];
|
||||
player._finishCallback(audioID, *audioInfo.filePath);
|
||||
}
|
||||
|
||||
AudioEngine::remove(audioID);
|
||||
it = _audioPlayers.erase(it);
|
||||
}
|
||||
else{
|
||||
player.update();
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (_audioPlayers.empty()){
|
||||
_lazyInitLoop = true;
|
||||
|
||||
auto scheduler = cocos2d::Director::getInstance()->getScheduler();
|
||||
scheduler->unschedule(schedule_selector(AudioEngineImpl::update), this);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEngineImpl::uncache(const std::string &filePath)
|
||||
{
|
||||
_audioCaches.erase(filePath);
|
||||
}
|
||||
|
||||
void AudioEngineImpl::uncacheAll()
|
||||
{
|
||||
_audioCaches.clear();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* cocos2d-x http://www.cocos2d-x.org
|
||||
*
|
||||
* Copyright (c) 2010-2011 - cocos2d-x community
|
||||
*
|
||||
* Portions Copyright (c) Microsoft Open Technologies, Inc.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
|
||||
#ifndef __AUDIO_ENGINE_WINRT_H_
|
||||
#define __AUDIO_ENGINE_WINRT_H_
|
||||
|
||||
#define NEAR near
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "base/CCRef.h"
|
||||
#include "AudioEngine.h"
|
||||
#include "AudioCachePlayer.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
namespace experimental{
|
||||
#define MAX_AUDIOINSTANCES 32
|
||||
|
||||
|
||||
class AudioEngineThreadPool;
|
||||
|
||||
class CC_DLL AudioEngineImpl : public cocos2d::Ref
|
||||
{
|
||||
public:
|
||||
AudioEngineImpl();
|
||||
~AudioEngineImpl();
|
||||
|
||||
bool init();
|
||||
int play2d(const std::string &fileFullPath, bool loop, float volume);
|
||||
void setVolume(int audioID, float volume);
|
||||
void setLoop(int audioID, bool loop);
|
||||
bool pause(int audioID);
|
||||
bool resume(int audioID);
|
||||
bool stop(int audioID);
|
||||
void stopAll();
|
||||
float getDuration(int audioID);
|
||||
float getCurrentTime(int audioID);
|
||||
bool setCurrentTime(int audioID, float time);
|
||||
void setFinishCallback(int audioID, const std::function<void(int, const std::string &)> &callback);
|
||||
void uncache(const std::string& filePath);
|
||||
void uncacheAll();
|
||||
void update(float dt);
|
||||
|
||||
private:
|
||||
void _play2d(AudioCache *cache, int audioID);
|
||||
|
||||
private:
|
||||
std::unordered_map<int, AudioPlayer> _audioPlayers;
|
||||
std::unordered_map<std::string, AudioCache> _audioCaches;
|
||||
std::vector<AudioCache*> _toRemoveCaches;
|
||||
|
||||
std::mutex _threadMutex;
|
||||
std::vector<int> _toRemoveAudioIDs;
|
||||
bool _lazyInitLoop;
|
||||
int _currentAudioID;
|
||||
AudioEngineThreadPool* _threadPool;
|
||||
};
|
||||
}
|
||||
|
||||
NS_CC_END
|
||||
#endif // __AUDIO_ENGINE_WINRT_H_
|
||||
#endif
|
||||
|
|
@ -0,0 +1,198 @@
|
|||
/*
|
||||
* cocos2d-x http://www.cocos2d-x.org
|
||||
*
|
||||
* Copyright (c) 2010-2011 - cocos2d-x community
|
||||
*
|
||||
* Portions Copyright (c) Microsoft Open Technologies, Inc.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
|
||||
#include "AudioSourceReader.h"
|
||||
|
||||
using namespace cocos2d;
|
||||
using namespace cocos2d::experimental;
|
||||
|
||||
|
||||
// AudioFileReader
|
||||
AudioSourceReader::AudioSourceReader() :
|
||||
_isStreaming(false)
|
||||
, _filePath("")
|
||||
, _audioSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
AudioSourceReader::~AudioSourceReader()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// WAVFileReader
|
||||
WAVReader::WAVReader() :
|
||||
_bytesRead(0)
|
||||
, _isDirty(false)
|
||||
, _streamer(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
WAVReader::~WAVReader()
|
||||
{
|
||||
flushChunks();
|
||||
}
|
||||
|
||||
bool WAVReader::initialize(const std::string& filePath)
|
||||
{
|
||||
bool ret = false;
|
||||
_isStreaming = false;
|
||||
_filePath = filePath;
|
||||
|
||||
do {
|
||||
auto fileSize = FileUtils::getInstance()->getFileSize(_filePath);
|
||||
|
||||
if (fileSize <= 0)
|
||||
break;
|
||||
|
||||
flushChunks();
|
||||
|
||||
_rwMutex.lock();
|
||||
_streamer = ref new MediaStreamer;
|
||||
_streamer->Initialize(std::wstring(_filePath.begin(), _filePath.end()).c_str(), true);
|
||||
_rwMutex.unlock();
|
||||
_wfx = _streamer->GetOutputWaveFormatEx();
|
||||
UINT32 dataSize = _streamer->GetMaxStreamLengthInBytes();
|
||||
|
||||
if (dataSize <= 0)
|
||||
break;
|
||||
|
||||
_audioSize = dataSize;
|
||||
if (_audioSize <= PCMDATA_CACHEMAXSIZE) {
|
||||
produceChunk();
|
||||
}
|
||||
else {
|
||||
_isStreaming = true;
|
||||
for (int i = 0; i < QUEUEBUFFER_NUM; i++) {
|
||||
produceChunk();
|
||||
}
|
||||
}
|
||||
|
||||
ret = true;
|
||||
} while (false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WAVReader::consumeChunk(AudioDataChunk& chunk)
|
||||
{
|
||||
bool ret = false;
|
||||
_isDirty = true;
|
||||
|
||||
_rwMutex.lock();
|
||||
if (_chnkQ.size() > 0) {
|
||||
chunk = _chnkQ.front();
|
||||
if (_isStreaming) {
|
||||
_chnkQ.pop();
|
||||
}
|
||||
else{
|
||||
// copy for future requests
|
||||
auto cpy = new (std::nothrow) unsigned char[chunk._dataSize];
|
||||
memcpy(cpy, chunk._data, chunk._dataSize);
|
||||
_chnkQ.front()._data = cpy;
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
_rwMutex.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void WAVReader::produceChunk()
|
||||
{
|
||||
_rwMutex.lock();
|
||||
int chunkSize = _audioSize;
|
||||
|
||||
do {
|
||||
if (!_isStreaming && _chnkQ.size() || _chnkQ.size() >= QUEUEBUFFER_NUM) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (_isStreaming) {
|
||||
chunkSize = std::min(CHUNK_SIZE_MAX, _audioSize - _bytesRead);
|
||||
}
|
||||
|
||||
if (!chunkSize && !_chnkQ.size()) {
|
||||
auto alignment = _wfx.nChannels * _wfx.nBlockAlign;
|
||||
_bytesRead -= alignment;
|
||||
chunkSize = alignment;
|
||||
}
|
||||
|
||||
UINT retSize = 0;
|
||||
AudioDataChunk chunk = { 0 };
|
||||
chunk._data = new (std::nothrow) unsigned char[chunkSize];
|
||||
_streamer->ReadChunk(chunk._data, _bytesRead, chunkSize, &retSize);
|
||||
|
||||
if (retSize <= 0) {
|
||||
delete[] chunk._data;
|
||||
chunk._data = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
_bytesRead += retSize;
|
||||
chunk._dataSize = retSize;
|
||||
chunk._seqNo = ((float)_bytesRead / _audioSize) * ((float)_audioSize / CHUNK_SIZE_MAX);
|
||||
chunk._endOfStream = (_bytesRead >= _audioSize);
|
||||
_chnkQ.push(chunk);
|
||||
} while (false);
|
||||
_rwMutex.unlock();
|
||||
}
|
||||
|
||||
void WAVReader::seekTo(const float ratio)
|
||||
{
|
||||
if (_isStreaming) {
|
||||
_bytesRead = ratio * _audioSize;
|
||||
|
||||
if (!_bytesRead && !_isDirty && _chnkQ.size()) // already in 0.0 position
|
||||
return;
|
||||
|
||||
flushChunks();
|
||||
|
||||
switch (_wfx.wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
case WAVE_FORMAT_ADPCM: {
|
||||
auto alignment = _wfx.nChannels * _wfx.nBlockAlign;
|
||||
_bytesRead = _bytesRead >= _audioSize ? (_audioSize - alignment) : _bytesRead - (_bytesRead % alignment);
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = 0; i < QUEUEBUFFER_NUM; i++) {
|
||||
produceChunk();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WAVReader::flushChunks()
|
||||
{
|
||||
_rwMutex.lock();
|
||||
while (!_chnkQ.empty()) {
|
||||
delete[] _chnkQ.front()._data;
|
||||
_chnkQ.pop();
|
||||
}
|
||||
_rwMutex.unlock();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* cocos2d-x http://www.cocos2d-x.org
|
||||
*
|
||||
* Copyright (c) 2010-2011 - cocos2d-x community
|
||||
*
|
||||
* Portions Copyright (c) Microsoft Open Technologies, Inc.
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
|
||||
#ifndef __AUDIO_SOURCE_READER_H_
|
||||
#define __AUDIO_SOURCE_READER_H_
|
||||
|
||||
#define NEAR near
|
||||
#include "MediaStreamer.h"
|
||||
|
||||
NS_CC_BEGIN
|
||||
namespace experimental{
|
||||
|
||||
const UINT PCMDATA_CACHEMAXSIZE = 2621440;
|
||||
const UINT QUEUEBUFFER_NUM = 4;
|
||||
const UINT CHUNK_SIZE_MAX = PCMDATA_CACHEMAXSIZE / QUEUEBUFFER_NUM;
|
||||
|
||||
enum class FileFormat
|
||||
{
|
||||
UNKNOWN,
|
||||
WAV,
|
||||
MP3,
|
||||
OGG
|
||||
};
|
||||
|
||||
typedef struct AudioDataChunk
|
||||
{
|
||||
size_t _dataSize;
|
||||
unsigned char *_data;
|
||||
bool _endOfStream;
|
||||
int _seqNo;
|
||||
} AudioDataChunk;
|
||||
|
||||
|
||||
class AudioSourceReader
|
||||
{
|
||||
public:
|
||||
AudioSourceReader();
|
||||
virtual ~AudioSourceReader();
|
||||
|
||||
bool isStreamingSource() { return _isStreaming; }
|
||||
std::string getFilePath() { return _filePath; }
|
||||
virtual size_t getTotalAudioBytes() { return _audioSize; }
|
||||
|
||||
virtual bool initialize(const std::string& filePath) = 0;
|
||||
virtual FileFormat getFileFormat() = 0;
|
||||
virtual bool consumeChunk(AudioDataChunk& chunk) = 0;
|
||||
virtual void produceChunk() = 0;
|
||||
virtual void seekTo(const float ratio) = 0;
|
||||
virtual const WAVEFORMATEX& getWaveFormatInfo() { return _wfx; }
|
||||
|
||||
protected:
|
||||
bool _isStreaming;
|
||||
std::string _filePath;
|
||||
size_t _audioSize;
|
||||
WAVEFORMATEX _wfx;
|
||||
};
|
||||
|
||||
class WAVReader : public AudioSourceReader
|
||||
{
|
||||
public:
|
||||
WAVReader();
|
||||
~WAVReader();
|
||||
|
||||
bool initialize(const std::string& filePath) override;
|
||||
FileFormat getFileFormat() override { return FileFormat::WAV; }
|
||||
bool consumeChunk(AudioDataChunk& chunk) override;
|
||||
void produceChunk() override;
|
||||
void seekTo(const float ratio) override;
|
||||
|
||||
protected:
|
||||
void enqueueChunk(const AudioDataChunk& chunk);
|
||||
void flushChunks();
|
||||
|
||||
private:
|
||||
bool _isDirty;
|
||||
size_t _bytesRead;
|
||||
std::mutex _rwMutex;
|
||||
MediaStreamer^ _streamer;
|
||||
std::queue<AudioDataChunk> _chnkQ;
|
||||
};
|
||||
|
||||
class MP3Reader : public AudioSourceReader
|
||||
{
|
||||
};
|
||||
|
||||
}
|
||||
NS_CC_END
|
||||
#endif // __AUDIO_SOURCE_READER_H_
|
||||
#endif
|
||||
|
|
@ -39,6 +39,8 @@ using namespace Concurrency;
|
|||
((uint32)(byte)(ch2) << 16) | ((uint32)(byte)(ch3) << 24 ))
|
||||
#endif /* defined(MAKEFOURCC) */
|
||||
|
||||
const int FMT_CHUNK_MAX = 256;
|
||||
|
||||
inline void ThrowIfFailed(HRESULT hr)
|
||||
{
|
||||
if (FAILED(hr))
|
||||
|
@ -50,6 +52,8 @@ inline void ThrowIfFailed(HRESULT hr)
|
|||
|
||||
MediaStreamer::MediaStreamer() :
|
||||
m_offset(0)
|
||||
, m_dataLen(0)
|
||||
, m_filename(nullptr)
|
||||
{
|
||||
ZeroMemory(&m_waveFormat, sizeof(m_waveFormat));
|
||||
m_location = Package::Current->InstalledLocation;
|
||||
|
@ -59,9 +63,13 @@ MediaStreamer::MediaStreamer() :
|
|||
MediaStreamer::~MediaStreamer()
|
||||
{
|
||||
}
|
||||
Platform::Array<byte>^ MediaStreamer::ReadData(
|
||||
_In_ Platform::String^ filename
|
||||
)
|
||||
|
||||
Platform::Array<byte>^ MediaStreamer::ReadData(_In_ Platform::String^ filename)
|
||||
{
|
||||
return ReadData(filename, 0, 0);
|
||||
}
|
||||
|
||||
Platform::Array<byte>^ MediaStreamer::ReadData(_In_ Platform::String^ filename, uint32 from, uint32 length)
|
||||
{
|
||||
CREATEFILE2_EXTENDED_PARAMETERS extendedParams = {0};
|
||||
extendedParams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
|
||||
|
@ -72,26 +80,16 @@ Platform::Array<byte>^ MediaStreamer::ReadData(
|
|||
extendedParams.hTemplateFile = nullptr;
|
||||
|
||||
Wrappers::FileHandle file(
|
||||
CreateFile2(
|
||||
filename->Data(),
|
||||
GENERIC_READ,
|
||||
FILE_SHARE_READ,
|
||||
OPEN_EXISTING,
|
||||
&extendedParams
|
||||
)
|
||||
CreateFile2(filename->Data(), GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, &extendedParams)
|
||||
);
|
||||
|
||||
if (file.Get()==INVALID_HANDLE_VALUE)
|
||||
{
|
||||
throw ref new Platform::FailureException();
|
||||
}
|
||||
|
||||
FILE_STANDARD_INFO fileInfo = {0};
|
||||
if (!GetFileInformationByHandleEx(
|
||||
file.Get(),
|
||||
FileStandardInfo,
|
||||
&fileInfo,
|
||||
sizeof(fileInfo)
|
||||
))
|
||||
FILE_STANDARD_INFO fileInfo = { 0 };
|
||||
if (!GetFileInformationByHandleEx(file.Get(), FileStandardInfo, &fileInfo, sizeof(fileInfo)))
|
||||
{
|
||||
throw ref new Platform::FailureException();
|
||||
}
|
||||
|
@ -101,15 +99,21 @@ Platform::Array<byte>^ MediaStreamer::ReadData(
|
|||
throw ref new Platform::OutOfMemoryException();
|
||||
}
|
||||
|
||||
Platform::Array<byte>^ fileData = ref new Platform::Array<byte>(fileInfo.EndOfFile.LowPart);
|
||||
from += m_offset;
|
||||
length = (length == 0 || from + length > fileInfo.EndOfFile.LowPart) ? fileInfo.EndOfFile.LowPart - from : length;
|
||||
Platform::Array<byte>^ fileData = ref new Platform::Array<byte>(length);
|
||||
|
||||
if (!ReadFile(
|
||||
file.Get(),
|
||||
fileData->Data,
|
||||
fileData->Length,
|
||||
nullptr,
|
||||
nullptr
|
||||
) )
|
||||
if (from)
|
||||
{
|
||||
LARGE_INTEGER pos = { 0 };
|
||||
pos.QuadPart = from;
|
||||
if (!SetFilePointerEx(file.Get(), pos, nullptr, FILE_BEGIN))
|
||||
{
|
||||
throw ref new Platform::FailureException();
|
||||
}
|
||||
}
|
||||
|
||||
if (!ReadFile(file.Get(), fileData->Data, fileData->Length, nullptr, nullptr))
|
||||
{
|
||||
throw ref new Platform::FailureException();
|
||||
}
|
||||
|
@ -117,9 +121,9 @@ Platform::Array<byte>^ MediaStreamer::ReadData(
|
|||
return fileData;
|
||||
}
|
||||
|
||||
void MediaStreamer::Initialize(__in const WCHAR* url)
|
||||
void MediaStreamer::Initialize(__in const WCHAR* url, bool lazy)
|
||||
{
|
||||
|
||||
m_filename = ref new Platform::String(url);
|
||||
WCHAR filePath[MAX_PATH] = {0};
|
||||
if ((wcslen(url) > 1 && url[1] == ':'))
|
||||
{
|
||||
|
@ -139,8 +143,7 @@ void MediaStreamer::Initialize(__in const WCHAR* url)
|
|||
wcscat_s(filePath, url);
|
||||
}
|
||||
|
||||
|
||||
Platform::Array<byte>^ data = ReadData(ref new Platform::String(filePath));
|
||||
Platform::Array<byte>^ data = lazy ? ReadData(ref new Platform::String(filePath), 0, FMT_CHUNK_MAX) : ReadData(ref new Platform::String(filePath));
|
||||
UINT32 length = data->Length;
|
||||
const byte * dataPtr = data->Data;
|
||||
UINT32 offset = 0;
|
||||
|
@ -197,22 +200,45 @@ void MediaStreamer::Initialize(__in const WCHAR* url)
|
|||
|
||||
// Locate the 'data' chunk and copy its contents to a buffer.
|
||||
ThrowIfFailed(ReadChunk(MAKEFOURCC('d', 'a', 't', 'a'), chunkSize, chunkPos));
|
||||
m_data.resize(chunkSize);
|
||||
CopyMemory(m_data.data(), &dataPtr[chunkPos], chunkSize);
|
||||
m_dataLen = chunkSize;
|
||||
m_offset = chunkPos;
|
||||
|
||||
m_offset = 0;
|
||||
if (!lazy)
|
||||
{
|
||||
m_data.resize(chunkSize);
|
||||
CopyMemory(m_data.data(), &dataPtr[chunkPos], chunkSize);
|
||||
m_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStreamer::ReadAll(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength)
|
||||
{
|
||||
UINT32 toCopy = m_data.size() - m_offset;
|
||||
if (toCopy > maxBufferSize) toCopy = maxBufferSize;
|
||||
if (!m_data.size())
|
||||
{
|
||||
ReadChunk(buffer, 0, m_dataLen, bufferLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
UINT32 toCopy = m_data.size() - m_offset;
|
||||
if (toCopy > maxBufferSize) toCopy = maxBufferSize;
|
||||
|
||||
CopyMemory(buffer, m_data.data(), toCopy);
|
||||
*bufferLength = toCopy;
|
||||
CopyMemory(buffer, m_data.data(), toCopy);
|
||||
*bufferLength = toCopy;
|
||||
|
||||
m_offset += toCopy;
|
||||
if (m_offset > m_data.size()) m_offset = m_data.size();
|
||||
m_offset += toCopy;
|
||||
if (m_offset > m_data.size()) m_offset = m_data.size();
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStreamer::ReadChunk(uint8* buffer, uint32 from, uint32 length, uint32* bytesRead)
|
||||
{
|
||||
Platform::Array<byte>^ data = ReadData(m_filename, from, length);
|
||||
*bytesRead = data->Length;
|
||||
|
||||
if (*bytesRead > 0)
|
||||
{
|
||||
CopyMemory(buffer, (byte*)data->Data, data->Length);
|
||||
}
|
||||
}
|
||||
|
||||
void MediaStreamer::Restart()
|
||||
|
|
|
@ -29,9 +29,12 @@ private:
|
|||
uint32 m_maxStreamLengthInBytes;
|
||||
std::vector<byte> m_data;
|
||||
UINT32 m_offset;
|
||||
Platform::Array<byte>^ ReadData(
|
||||
_In_ Platform::String^ filename
|
||||
);
|
||||
UINT32 m_dataLen;
|
||||
Platform::String^ m_filename;
|
||||
|
||||
Platform::Array<byte>^ ReadData(_In_ Platform::String^ filename);
|
||||
Platform::Array<byte>^ ReadData(_In_ Platform::String^ filename, uint32 from, uint32 length);
|
||||
|
||||
internal:
|
||||
Windows::Storage::StorageFolder^ m_location;
|
||||
Platform::String^ m_locationPath;
|
||||
|
@ -49,10 +52,11 @@ internal:
|
|||
|
||||
UINT32 GetMaxStreamLengthInBytes()
|
||||
{
|
||||
return m_data.size();
|
||||
return m_dataLen;
|
||||
}
|
||||
|
||||
void Initialize(_In_ const WCHAR* url);
|
||||
void ReadAll(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength);
|
||||
void Initialize(_In_ const WCHAR* url, bool lazy = false);
|
||||
void ReadAll(uint8* buffer, uint32 maxBufferSize, uint32* bufferLength);
|
||||
void ReadChunk(uint8* buffer, uint32 from, uint32 length, uint32* bytesRead);
|
||||
void Restart();
|
||||
};
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
||||
#include "NewAudioEngineTest.h"
|
||||
#include "ui/CocosGUI.h"
|
||||
|
@ -288,7 +288,11 @@ bool AudioControlTest::init()
|
|||
|
||||
auto playItem = TextButton::create("play", [&](TextButton* button){
|
||||
if (_audioID == AudioEngine::INVALID_AUDIO_ID) {
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
_audioID = AudioEngine::play2d("background.wav", _loopEnabled, _volume);
|
||||
#else
|
||||
_audioID = AudioEngine::play2d("background.mp3", _loopEnabled, _volume);
|
||||
#endif
|
||||
|
||||
if(_audioID != AudioEngine::INVALID_AUDIO_ID) {
|
||||
button->setEnabled(false);
|
||||
|
@ -350,7 +354,11 @@ bool AudioControlTest::init()
|
|||
addChild(loopItem);
|
||||
|
||||
auto uncacheItem = TextButton::create("uncache", [&](TextButton* button){
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
AudioEngine::uncache("background.wav");
|
||||
#else
|
||||
AudioEngine::uncache("background.mp3");
|
||||
#endif
|
||||
|
||||
_audioID = AudioEngine::INVALID_AUDIO_ID;
|
||||
((TextButton*)_playItem)->setEnabled(true);
|
||||
|
@ -440,7 +448,11 @@ bool PlaySimultaneouslyTest::init()
|
|||
char text[36];
|
||||
int tmp = 81;
|
||||
for(int index = 0; index < TEST_COUNT; ++index){
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
sprintf(text, "audio/SoundEffectsFX009/FX0%d.wav", tmp + index);
|
||||
#else
|
||||
sprintf(text,"audio/SoundEffectsFX009/FX0%d.mp3",tmp + index);
|
||||
#endif
|
||||
_files[index] = text;
|
||||
}
|
||||
_playingcount = 0;
|
||||
|
@ -490,7 +502,11 @@ bool AudioProfileTest::init()
|
|||
auto ret = AudioEngineTestDemo::init();
|
||||
|
||||
char text[30];
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
_files[0] = "background.wav";
|
||||
#else
|
||||
_files[0] = "background.mp3";
|
||||
#endif
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC
|
||||
_files[1] = "background.caf";
|
||||
#else
|
||||
|
@ -627,7 +643,11 @@ bool LargeAudioFileTest::init()
|
|||
auto ret = AudioEngineTestDemo::init();
|
||||
|
||||
auto playItem = TextButton::create("play large audio file", [&](TextButton* button){
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT
|
||||
AudioEngine::play2d("audio/LuckyDay.wav");
|
||||
#else
|
||||
AudioEngine::play2d("audio/LuckyDay.mp3");
|
||||
#endif
|
||||
});
|
||||
playItem->setNormalizedPosition(Vec2::ANCHOR_MIDDLE);
|
||||
this->addChild(playItem);
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
****************************************************************************/
|
||||
|
||||
#include "platform/CCPlatformConfig.h"
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
#if CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
|
||||
|
||||
#ifndef __NEWAUDIOENGINE_TEST_H_
|
||||
#define __NEWAUDIOENGINE_TEST_H_
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
addTest("Actions - Progress", [](){return new (std::nothrow) ActionsProgressTests(); });
|
||||
addTest("Allocator - Basic", [](){return new (std::nothrow) AllocatorTests(); });
|
||||
addTest("Audio - CocosDenshion", []() { return new (std::nothrow) CocosDenshionTests(); });
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
addTest("Audio - NewAudioEngine", []() { return new (std::nothrow) AudioEngineTests(); });
|
||||
#endif
|
||||
#if CC_ENABLE_BOX2D_INTEGRATION
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
#include "PerformanceTest/PerformanceTest.h"
|
||||
#include "ZwoptexTest/ZwoptexTest.h"
|
||||
#include "CocosDenshionTest/CocosDenshionTest.h"
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID || CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_MAC || CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
|
||||
#include "NewAudioEngineTest/NewAudioEngineTest.h"
|
||||
#endif
|
||||
#if (CC_TARGET_PLATFORM != CC_PLATFORM_EMSCRIPEN)
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -110,6 +110,7 @@
|
|||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\MenuTest\MenuTest.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\MotionStreakTest\MotionStreakTest.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\MutiTouchTest\MutiTouchTest.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\NewAudioEngineTest\NewAudioEngineTest.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\NewEventDispatcherTest\NewEventDispatcherTest.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\NewRendererTest\NewRendererTest.cpp" />
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\NodeTest\NodeTest.cpp" />
|
||||
|
@ -375,6 +376,7 @@
|
|||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\MenuTest\MenuTest.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\MotionStreakTest\MotionStreakTest.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\MutiTouchTest\MutiTouchTest.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\NewAudioEngineTest\NewAudioEngineTest.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\NewEventDispatcherTest\NewEventDispatcherTest.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\NewRendererTest\NewRendererTest.h" />
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\NodeTest\NodeTest.h" />
|
||||
|
|
|
@ -787,6 +787,9 @@
|
|||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\Physics3DTest\Physics3DTest.cpp">
|
||||
<Filter>Classes\Physics3DTest</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\NewAudioEngineTest\NewAudioEngineTest.cpp">
|
||||
<Filter>Classes\NewAudioEngineTest</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\ActionManagerTest\ActionManagerTest.cpp">
|
||||
|
@ -1743,5 +1746,8 @@
|
|||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\Physics3DTest\Physics3DTest.h">
|
||||
<Filter>Classes\Physics3DTest</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\NewAudioEngineTest\NewAudioEngineTest.h">
|
||||
<Filter>Classes\NewAudioEngineTest</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue