Merge pull request #11893 from MSOpenTech/v3-winrt-audio

New audio engine support in winrt win8.1-universal
This commit is contained in:
minggo 2015-05-20 13:40:41 +08:00
commit 7024d3129c
29 changed files with 1690 additions and 53 deletions

View File

@ -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>

View File

@ -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" />

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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();
};

View File

@ -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);

View File

@ -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_

View File

@ -24,7 +24,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

View File

@ -41,7 +41,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.

View File

@ -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" />
@ -376,6 +377,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" />

View File

@ -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>
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\Classes\Scene3DTest\Scene3DTest.cpp">
<Filter>Classes\Scene3DTest</Filter>
</ClCompile>
@ -1749,6 +1752,9 @@
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\Physics3DTest\Physics3DTest.h">
<Filter>Classes\Physics3DTest</Filter>
</ClInclude>
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\NewAudioEngineTest\NewAudioEngineTest.h">
<Filter>Classes\NewAudioEngineTest</Filter>
</ClInclude>
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\Classes\Scene3DTest\Scene3DTest.h">
<Filter>Classes\Scene3DTest</Filter>
</ClInclude>