diff --git a/.gitignore b/.gitignore index 4ed594f0..5bf2f7c3 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,9 @@ ipch/ *.user *.lnk + .vs -.vscode \ No newline at end of file +.vscode + +# NuGet packages +packages/ \ No newline at end of file diff --git a/Easy2D-Audio/Easy2D-Audio.vcxproj b/Easy2D-Audio/Easy2D-Audio.vcxproj new file mode 100644 index 00000000..2a78cd1b --- /dev/null +++ b/Easy2D-Audio/Easy2D-Audio.vcxproj @@ -0,0 +1,162 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5D45F66B-BECD-4909-BCFC-C0F3CED9D0A2} + Easy2D-Audio + + + + StaticLibrary + true + Unicode + + + StaticLibrary + false + true + Unicode + + + StaticLibrary + true + Unicode + + + StaticLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + true + ../Easy2D + + + Windows + true + + + + + + + Level3 + Disabled + true + ../Easy2D + + + Windows + true + + + + + + + Level3 + MaxSpeed + true + true + true + ../Easy2D + + + Windows + true + true + true + + + + + + + Level3 + MaxSpeed + true + true + true + ../Easy2D + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Easy2D-Audio/Easy2D-Audio.vcxproj.filters b/Easy2D-Audio/Easy2D-Audio.vcxproj.filters new file mode 100644 index 00000000..883e2fa0 --- /dev/null +++ b/Easy2D-Audio/Easy2D-Audio.vcxproj.filters @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Easy2D-Audio/Music.cpp b/Easy2D-Audio/Music.cpp new file mode 100644 index 00000000..002a6a1f --- /dev/null +++ b/Easy2D-Audio/Music.cpp @@ -0,0 +1,177 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "easy2d-audio.h" +#include "Music.h" +#include "Transcoder.h" + +namespace easy2d +{ + Music::Music() + : opened_(false) + , playing_(false) + , wave_data_(nullptr) + , size_(0) + , voice_(nullptr) + { + } + + Music::Music(Resource const& res) + : Music() + { + Load(res); + } + + Music::~Music() + { + Close(); + } + + bool Music::Load(Resource const& res) + { + if (opened_) + { + Close(); + } + + HRESULT hr = S_OK; + Transcoder transcoder; + + if (res.IsFileType()) + { + if (!modules::Shlwapi::Get().PathFileExistsW(res.GetFileName().c_str())) + { + E2D_WARNING_LOG(L"Media file '%s' not found", res.GetFileName().c_str()); + return false; + } + hr = transcoder.LoadMediaFile(res.GetFileName(), &wave_data_, &size_); + } + else + { + hr = transcoder.LoadMediaResource(res, &wave_data_, &size_); + } + + if (FAILED(hr)) + { + E2D_ERROR_LOG(L"Load media file failed with HRESULT of %08X", hr); + return false; + } + + hr = Audio::Instance().CreateVoice(voice_, transcoder.GetWaveFormatEx()); + if (FAILED(hr)) + { + if (wave_data_) + { + delete[] wave_data_; + wave_data_ = nullptr; + } + E2D_ERROR_LOG(L"Create source voice failed with HRESULT of %08X", hr); + return false; + } + + opened_ = true; + return true; + } + + bool Music::Play(int loop_count) + { + if (!opened_) + { + E2D_ERROR_LOG(L"Music must be opened first!"); + return false; + } + + UINT32 buffers_queued = 0; + voice_.GetBuffersQueued(&buffers_queued); + if (buffers_queued) + Stop(); + + if (loop_count < 0) + loop_count = XAUDIO2_LOOP_INFINITE; + else + loop_count = std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1); + + HRESULT hr = voice_.Play(wave_data_, size_, static_cast(loop_count)); + if (FAILED(hr)) + { + E2D_ERROR_LOG(L"Submitting source buffer failed with HRESULT of %08X", hr); + } + + playing_ = SUCCEEDED(hr); + + return playing_; + } + + void Music::Pause() + { + if (SUCCEEDED(voice_.Pause())) + playing_ = false; + } + + void Music::Resume() + { + if (SUCCEEDED(voice_.Resume())) + playing_ = true; + } + + void Music::Stop() + { + if (SUCCEEDED(voice_.Stop())) + playing_ = false; + } + + void Music::Close() + { + voice_.Destroy(); + + if (wave_data_) + { + delete[] wave_data_; + wave_data_ = nullptr; + } + + opened_ = false; + playing_ = false; + } + + bool Music::IsPlaying() const + { + if (opened_) + { + UINT32 buffers_queued = 0; + voice_.GetBuffersQueued(&buffers_queued); + if (buffers_queued && playing_) + return true; + } + return false; + } + + float Music::GetVolume() const + { + float volume = 0.f; + voice_.GetVolume(&volume); + return volume; + } + + bool Music::SetVolume(float volume) + { + return SUCCEEDED(voice_.SetVolume(volume)); + } +} \ No newline at end of file diff --git a/Easy2D-Audio/Music.h b/Easy2D-Audio/Music.h new file mode 100644 index 00000000..50db0a5a --- /dev/null +++ b/Easy2D-Audio/Music.h @@ -0,0 +1,81 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include "Voice.h" + +namespace easy2d +{ + E2D_DECLARE_SMART_PTR(Music); + + // ֶ + class E2D_API Music + : public virtual Object + { + public: + Music(); + + Music( + Resource const& res /* Դ */ + ); + + virtual ~Music(); + + // Դ + bool Load( + Resource const& res /* Դ */ + ); + + // + bool Play( + int loop_count = 0 /* ѭ (-1 Ϊѭ) */ + ); + + // ͣ + void Pause(); + + // + void Resume(); + + // ֹͣ + void Stop(); + + // رղԴ + void Close(); + + // Ƿڲ + bool IsPlaying() const; + + // ȡ + float GetVolume() const; + + // + bool SetVolume( + float volume /* 1 Ϊԭʼ, 1 ΪŴ, 0 ΪС */ + ); + + protected: + bool opened_; + bool playing_; + UINT32 size_; + BYTE* wave_data_; + Voice voice_; + }; +} diff --git a/Easy2D-Audio/Player.cpp b/Easy2D-Audio/Player.cpp new file mode 100644 index 00000000..e49e2469 --- /dev/null +++ b/Easy2D-Audio/Player.cpp @@ -0,0 +1,141 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "easy2d-audio.h" +#include "Player.h" + +namespace easy2d +{ + Player::Player() + : volume_(1.f) + { + } + + Player::~Player() + { + ClearCache(); + } + + bool Player::Load(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (musics_cache_.end() != musics_cache_.find(hash_code)) + return true; + + MusicPtr music = new (std::nothrow) Music(); + + if (music) + { + if (music->Load(res)) + { + music->SetVolume(volume_); + musics_cache_.insert(std::make_pair(hash_code, music)); + return true; + } + } + return false; + } + + bool Player::Play(Resource const& res, int loop_count) + { + if (Load(res)) + { + size_t hash_code = res.GetHashCode(); + auto music = musics_cache_[hash_code]; + if (music->Play(loop_count)) + { + return true; + } + } + return false; + } + + void Player::Pause(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (musics_cache_.end() != musics_cache_.find(hash_code)) + musics_cache_[hash_code]->Pause(); + } + + void Player::Resume(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (musics_cache_.end() != musics_cache_.find(hash_code)) + musics_cache_[hash_code]->Resume(); + } + + void Player::Stop(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (musics_cache_.end() != musics_cache_.find(hash_code)) + musics_cache_[hash_code]->Stop(); + } + + bool Player::IsPlaying(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (musics_cache_.end() != musics_cache_.find(hash_code)) + return musics_cache_[hash_code]->IsPlaying(); + return false; + } + + float Player::GetVolume() const + { + return volume_; + } + + void Player::SetVolume(float volume) + { + volume_ = std::min(std::max(volume, -224.f), 224.f); + for (const auto& pair : musics_cache_) + { + pair.second->SetVolume(volume_); + } + } + + void Player::PauseAll() + { + for (const auto& pair : musics_cache_) + { + pair.second->Pause(); + } + } + + void Player::ResumeAll() + { + for (const auto& pair : musics_cache_) + { + pair.second->Resume(); + } + } + + void Player::StopAll() + { + for (const auto& pair : musics_cache_) + { + pair.second->Stop(); + } + } + + void Player::ClearCache() + { + musics_cache_.clear(); + } +} \ No newline at end of file diff --git a/Easy2D-Audio/Player.h b/Easy2D-Audio/Player.h new file mode 100644 index 00000000..004310b9 --- /dev/null +++ b/Easy2D-Audio/Player.h @@ -0,0 +1,93 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once + +namespace easy2d +{ + E2D_DECLARE_SMART_PTR(Player); + + // ֲ + class E2D_API Player + : protected Object + { + using MusicMap = Map; + + public: + Player(); + + ~Player(); + + // ԤԴ + bool Load( + Resource const& res /* Դ */ + ); + + // + bool Play( + Resource const& res, /* Դ */ + int loop_count = 0 /* ѭ (-1 Ϊѭ) */ + ); + + // ͣ + void Pause( + Resource const& res /* Դ */ + ); + + // + void Resume( + Resource const& res /* Դ */ + ); + + // ֹͣ + void Stop( + Resource const& res /* Դ */ + ); + + // ȡֲ״̬ + bool IsPlaying( + Resource const& res /* Դ */ + ); + + // ȡ + float GetVolume() const; + + // + void SetVolume( + float volume /* 1.0 Ϊԭʼ */ + ); + + // ͣ + void PauseAll(); + + // + void ResumeAll(); + + // ֹͣ + void StopAll(); + + // + void ClearCache(); + + protected: + float volume_; + MusicMap musics_cache_; + }; +} diff --git a/Easy2D-Audio/Transcoder.cpp b/Easy2D-Audio/Transcoder.cpp new file mode 100644 index 00000000..710af127 --- /dev/null +++ b/Easy2D-Audio/Transcoder.cpp @@ -0,0 +1,267 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#ifndef INITGUID +# define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio +#endif + +#include "easy2d-audio.h" +#include "Transcoder.h" +#include "audio-modules.h" + +namespace easy2d +{ + Transcoder::Transcoder() + : wave_format_(nullptr) + { + } + + Transcoder::~Transcoder() + { + if (wave_format_) + { + ::CoTaskMemFree(wave_format_); + wave_format_ = nullptr; + } + } + + const WAVEFORMATEX* Transcoder::GetWaveFormatEx() const + { + return wave_format_; + } + + HRESULT Transcoder::LoadMediaFile(String const& file_path, BYTE** wave_data, UINT32* wave_data_size) + { + HRESULT hr = S_OK; + + ComPtr reader; + + hr = modules::MediaFoundation::Get().MFCreateSourceReaderFromURL( + file_path.c_str(), + nullptr, + &reader + ); + + if (SUCCEEDED(hr)) + { + hr = ReadSource(reader.Get(), wave_data, wave_data_size); + } + + return hr; + } + + HRESULT Transcoder::LoadMediaResource(Resource const& res, BYTE** wave_data, UINT32* wave_data_size) + { + HRESULT hr = S_OK; + + ComPtr stream; + ComPtr byte_stream; + ComPtr reader; + + LPVOID buffer; + DWORD buffer_size; + if (!res.Load(buffer, buffer_size)) { return false; } + + stream = modules::Shlwapi::Get().SHCreateMemStream( + static_cast(buffer), + static_cast(buffer_size) + ); + + if (stream == nullptr) + { + E2D_ERROR_LOG(L"SHCreateMemStream failed"); + return E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + hr = modules::MediaFoundation::Get().MFCreateMFByteStreamOnStream(stream.Get(), &byte_stream); + } + + if (SUCCEEDED(hr)) + { + hr = modules::MediaFoundation::Get().MFCreateSourceReaderFromByteStream( + byte_stream.Get(), + nullptr, + &reader + ); + } + + if (SUCCEEDED(hr)) + { + hr = ReadSource(reader.Get(), wave_data, wave_data_size); + } + + return hr; + } + + HRESULT Transcoder::ReadSource(IMFSourceReader* reader, BYTE** wave_data, UINT32* wave_data_size) + { + HRESULT hr = S_OK; + DWORD max_stream_size = 0; + + ComPtr partial_type; + ComPtr uncompressed_type; + + hr = modules::MediaFoundation::Get().MFCreateMediaType(&partial_type); + + if (SUCCEEDED(hr)) + { + hr = partial_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); + } + + if (SUCCEEDED(hr)) + { + hr = partial_type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); + } + + // source reader ýͣʹúʵĽȥƵ + if (SUCCEEDED(hr)) + { + hr = reader->SetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + 0, + partial_type.Get() + ); + } + + // IMFMediaType лȡ WAVEFORMAT ṹ + if (SUCCEEDED(hr)) + { + hr = reader->GetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + &uncompressed_type + ); + } + + // ָƵ + if (SUCCEEDED(hr)) + { + hr = reader->SetStreamSelection( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + true + ); + } + + // ȡ WAVEFORMAT + if (SUCCEEDED(hr)) + { + UINT32 size = 0; + hr = modules::MediaFoundation::Get().MFCreateWaveFormatExFromMFMediaType( + uncompressed_type.Get(), + &wave_format_, + &size, + (DWORD)MFWaveFormatExConvertFlag_Normal + ); + } + + // ƵС + if (SUCCEEDED(hr)) + { + PROPVARIANT prop; + PropVariantInit(&prop); + + hr = reader->GetPresentationAttribute( + (DWORD)MF_SOURCE_READER_MEDIASOURCE, + MF_PD_DURATION, + &prop + ); + + LONGLONG duration = prop.uhVal.QuadPart; + max_stream_size = static_cast( + (duration * wave_format_->nAvgBytesPerSec) / 10000000 + 1 + ); + PropVariantClear(&prop); + } + + // ȡƵ + if (SUCCEEDED(hr)) + { + DWORD flags = 0; + DWORD position = 0; + BYTE* data = new (std::nothrow) BYTE[max_stream_size]; + + ComPtr sample; + ComPtr buffer; + + if (data == nullptr) + { + E2D_ERROR_LOG(L"Low memory"); + hr = E_OUTOFMEMORY; + } + else + { + while (true) + { + hr = reader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + 0, + nullptr, + &flags, + nullptr, + &sample + ); + + if (flags & MF_SOURCE_READERF_ENDOFSTREAM) { break; } + + if (sample == nullptr) { continue; } + + if (SUCCEEDED(hr)) + { + hr = sample->ConvertToContiguousBuffer(&buffer); + + if (SUCCEEDED(hr)) + { + BYTE *audio_data = nullptr; + DWORD sample_buffer_length = 0; + + hr = buffer->Lock( + &audio_data, + nullptr, + &sample_buffer_length + ); + + if (SUCCEEDED(hr)) + { + for (DWORD i = 0; i < sample_buffer_length; i++) + { + data[position++] = audio_data[i]; + } + hr = buffer->Unlock(); + } + } + buffer = nullptr; + } + sample = nullptr; + + if (FAILED(hr)) { break; } + } + + if (SUCCEEDED(hr)) + { + *wave_data = data; + *wave_data_size = position; + } + } + } + + return hr; + } +} \ No newline at end of file diff --git a/Easy2D-Audio/Transcoder.h b/Easy2D-Audio/Transcoder.h new file mode 100644 index 00000000..dcfa53af --- /dev/null +++ b/Easy2D-Audio/Transcoder.h @@ -0,0 +1,57 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include +#include +#include + +namespace easy2d +{ + class E2D_API Transcoder + { + WAVEFORMATEX* wave_format_; + + public: + Transcoder(); + + ~Transcoder(); + + const WAVEFORMATEX* GetWaveFormatEx() const; + + HRESULT LoadMediaFile( + String const& file_path, + BYTE** wave_data, + UINT32* wave_data_size + ); + + HRESULT LoadMediaResource( + Resource const& res, + BYTE** wave_data, + UINT32* wave_data_size + ); + + HRESULT ReadSource( + IMFSourceReader* reader, + BYTE** wave_data, + UINT32* wave_data_size + ); + }; +} diff --git a/Easy2D-Audio/Voice.cpp b/Easy2D-Audio/Voice.cpp new file mode 100644 index 00000000..48dd3fe7 --- /dev/null +++ b/Easy2D-Audio/Voice.cpp @@ -0,0 +1,148 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "easy2d-audio.h" +#include "Voice.h" + +namespace easy2d +{ + Voice::Voice() + : source_voice_(nullptr) + { + } + + Voice::Voice(IXAudio2SourceVoice * source_voice) + : source_voice_(source_voice) + { + } + + Voice::~Voice() + { + Destroy(); + + Audio::Instance().DeleteVoice(this); + } + + HRESULT Voice::Play(const BYTE * wave_data, UINT32 data_size, UINT32 loop_count) + { + if (!source_voice_) + return E_UNEXPECTED; + + XAUDIO2_BUFFER buffer = { 0 }; + buffer.pAudioData = wave_data; + buffer.Flags = XAUDIO2_END_OF_STREAM; + buffer.AudioBytes = data_size; + buffer.LoopCount = loop_count; + + HRESULT hr = source_voice_->SubmitSourceBuffer(&buffer); + if (SUCCEEDED(hr)) + { + hr = source_voice_->Start(); + } + return hr; + } + + HRESULT Voice::Pause() + { + if (!source_voice_) + return E_UNEXPECTED; + + return source_voice_->Stop(); + } + + HRESULT Voice::Resume() + { + if (!source_voice_) + return E_UNEXPECTED; + + return source_voice_->Start(); + } + + HRESULT Voice::Stop() + { + if (!source_voice_) + return E_UNEXPECTED; + + HRESULT hr = source_voice_->Stop(); + if (SUCCEEDED(hr)) + { + hr = source_voice_->ExitLoop(); + } + + if (SUCCEEDED(hr)) + { + hr = source_voice_->FlushSourceBuffers(); + } + return hr; + } + + HRESULT Voice::GetVolume(float * volume) const + { + if (!source_voice_) + return E_UNEXPECTED; + + if (volume == nullptr) + return E_POINTER; + + source_voice_->GetVolume(volume); + return S_OK; + } + + HRESULT Voice::SetVolume(float volume) + { + if (!source_voice_) + return E_UNEXPECTED; + + volume = std::min(std::max(volume, -224.f), 224.f); + return source_voice_->SetVolume(volume); + } + + HRESULT Voice::GetBuffersQueued(UINT32 * queued) const + { + if (!source_voice_) + return E_UNEXPECTED; + + if (queued == nullptr) + return E_POINTER; + + XAUDIO2_VOICE_STATE state; + source_voice_->GetState(&state); + *queued = state.BuffersQueued; + return S_OK; + } + + void Voice::Destroy() + { + if (source_voice_) + { + source_voice_->Stop(); + source_voice_->FlushSourceBuffers(); + source_voice_->DestroyVoice(); + source_voice_ = nullptr; + } + } + + void Voice::SetSourceVoice(IXAudio2SourceVoice * source_voice) + { + Destroy(); + source_voice_ = source_voice; + } + +} \ No newline at end of file diff --git a/Easy2D-Audio/Voice.h b/Easy2D-Audio/Voice.h new file mode 100644 index 00000000..ce6cd830 --- /dev/null +++ b/Easy2D-Audio/Voice.h @@ -0,0 +1,72 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include + +namespace easy2d +{ + class E2D_API Voice + : protected Noncopyable + { + public: + Voice(); + + Voice( + IXAudio2SourceVoice* source_voice + ); + + ~Voice(); + + HRESULT Play( + const BYTE* wave_data, + UINT32 data_size, + UINT32 loop_count + ); + + HRESULT Pause(); + + HRESULT Resume(); + + HRESULT Stop(); + + HRESULT GetVolume( + float* volume + ) const; + + HRESULT SetVolume( + float volume + ); + + HRESULT GetBuffersQueued( + UINT32* queued + ) const; + + void Destroy(); + + void SetSourceVoice( + IXAudio2SourceVoice* source_voice + ); + + protected: + IXAudio2SourceVoice* source_voice_; + }; + +} diff --git a/Easy2D-Audio/audio-modules.cpp b/Easy2D-Audio/audio-modules.cpp new file mode 100644 index 00000000..983f9aa5 --- /dev/null +++ b/Easy2D-Audio/audio-modules.cpp @@ -0,0 +1,97 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "easy2d-audio.h" +#include "audio-modules.h" + +namespace easy2d +{ + namespace modules + { + XAudio2::XAudio2() + { + const auto xaudio2_dll_names = + { + L"xaudio2_9.dll", // for Windows 10 + L"xaudio2_8.dll", // for Windows 8 + L"xaudio2_7.dll" // for DirectX SDK + }; + + for (const auto& name : xaudio2_dll_names) + { + xaudio2 = LoadLibraryW(name); + if (xaudio2) + { + XAudio2Create = (PFN_XAudio2Create) + GetProcAddress(xaudio2, "XAudio2Create"); + break; + } + } + + if (!xaudio2) + { + E2D_ERROR_LOG(L"Load xaudio2.dll failed"); + throw std::runtime_error("Load xaudio2.dll failed"); + } + } + + MediaFoundation::MediaFoundation() + { + mfplat = LoadLibraryW(L"Mfplat.dll"); + if (mfplat) + { + MFStartup = (PFN_MFStartup) + GetProcAddress(mfplat, "MFStartup"); + + MFShutdown = (PFN_MFShutdown) + GetProcAddress(mfplat, "MFShutdown"); + + MFCreateMediaType = (PFN_MFCreateMediaType) + GetProcAddress(mfplat, "MFCreateMediaType"); + + MFCreateWaveFormatExFromMFMediaType = (PFN_MFCreateWaveFormatExFromMFMediaType) + GetProcAddress(mfplat, "MFCreateWaveFormatExFromMFMediaType"); + + MFCreateMFByteStreamOnStream = (PFN_MFCreateMFByteStreamOnStream) + GetProcAddress(mfplat, "MFCreateMFByteStreamOnStream"); + } + else + { + E2D_LOG(L"Load Mfplat.dll failed"); + throw std::runtime_error("Load Mfplat.dll failed"); + } + + mfreadwrite = LoadLibraryW(L"Mfreadwrite.dll"); + if (mfreadwrite) + { + MFCreateSourceReaderFromURL = (PFN_MFCreateSourceReaderFromURL) + GetProcAddress(mfreadwrite, "MFCreateSourceReaderFromURL"); + + MFCreateSourceReaderFromByteStream = (PFN_MFCreateSourceReaderFromByteStream) + GetProcAddress(mfreadwrite, "MFCreateSourceReaderFromByteStream"); + } + else + { + E2D_LOG(L"Load Mfreadwrite.dll failed"); + throw std::runtime_error("Load Mfreadwrite.dll failed"); + } + } + } +} \ No newline at end of file diff --git a/Easy2D-Audio/audio-modules.h b/Easy2D-Audio/audio-modules.h new file mode 100644 index 00000000..059d1535 --- /dev/null +++ b/Easy2D-Audio/audio-modules.h @@ -0,0 +1,83 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include +#include +#include +#include + +namespace easy2d +{ + namespace modules + { + class E2D_API XAudio2 + { + XAudio2(); + + HMODULE xaudio2; + + // XAudio2 functions + typedef HRESULT(WINAPI *PFN_XAudio2Create)(IXAudio2**, UINT32, XAUDIO2_PROCESSOR); + + public: + static inline XAudio2& Get() + { + static XAudio2 instance; + return instance; + } + + PFN_XAudio2Create XAudio2Create; + }; + + + class E2D_API MediaFoundation + { + MediaFoundation(); + + HMODULE mfplat; + HMODULE mfreadwrite; + + // MediaFoundation functions + typedef HRESULT(WINAPI *PFN_MFStartup)(ULONG, DWORD); + typedef HRESULT(WINAPI *PFN_MFShutdown)(); + typedef HRESULT(WINAPI *PFN_MFCreateMediaType)(IMFMediaType**); + typedef HRESULT(WINAPI *PFN_MFCreateWaveFormatExFromMFMediaType)(IMFMediaType*, WAVEFORMATEX**, UINT32*, UINT32); + typedef HRESULT(WINAPI *PFN_MFCreateSourceReaderFromURL)(LPCWSTR, IMFAttributes*, IMFSourceReader**); + typedef HRESULT(WINAPI *PFN_MFCreateSourceReaderFromByteStream)(IMFByteStream*, IMFAttributes*, IMFSourceReader**); + typedef HRESULT(WINAPI *PFN_MFCreateMFByteStreamOnStream)(IStream*, IMFByteStream**); + + public: + static inline MediaFoundation& Get() + { + static MediaFoundation instance; + return instance; + } + + PFN_MFStartup MFStartup; + PFN_MFShutdown MFShutdown; + PFN_MFCreateMediaType MFCreateMediaType; + PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType; + PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL; + PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream; + PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream; + }; + } +} diff --git a/Easy2D-Audio/audio.cpp b/Easy2D-Audio/audio.cpp new file mode 100644 index 00000000..233b1bd9 --- /dev/null +++ b/Easy2D-Audio/audio.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "easy2d-audio.h" +#include "audio.h" +#include "audio-modules.h" + +namespace easy2d +{ + Audio::Audio() + : x_audio2_(nullptr) + , mastering_voice_(nullptr) + { + } + + Audio::~Audio() + { + } + + void Audio::SetupComponent(Application*) + { + E2D_LOG(L"Creating audio resources"); + + HRESULT hr = modules::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL); + + if (SUCCEEDED(hr)) + { + hr = modules::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR); + } + + if (SUCCEEDED(hr)) + { + hr = x_audio2_->CreateMasteringVoice(&mastering_voice_); + } + + ThrowIfFailed(hr); + } + + void Audio::DestroyComponent() + { + E2D_LOG(L"Destroying audio resources"); + + ClearVoiceCache(); + + if (mastering_voice_) + { + mastering_voice_->DestroyVoice(); + mastering_voice_ = nullptr; + } + + if (x_audio2_) + { + x_audio2_->Release(); + x_audio2_ = nullptr; + } + + modules::MediaFoundation::Get().MFShutdown(); + } + + HRESULT Audio::CreateVoice(Voice& voice, const WAVEFORMATEX* wfx) + { + HRESULT hr; + IXAudio2SourceVoice* source_voice; + + hr = x_audio2_->CreateSourceVoice(&source_voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO); + if (SUCCEEDED(hr)) + { + voice.SetSourceVoice(source_voice); + voice_cache_.insert(&voice); + } + return hr; + } + + void Audio::DeleteVoice(Voice* voice) + { + voice_cache_.erase(voice); + } + + void Audio::ClearVoiceCache() + { + for (auto voice : voice_cache_) + { + voice->Destroy(); + } + voice_cache_.clear(); + } + + void Audio::Open() + { + x_audio2_->StartEngine(); + } + + void Audio::Close() + { + x_audio2_->StopEngine(); + } + +} \ No newline at end of file diff --git a/Easy2D-Audio/audio.h b/Easy2D-Audio/audio.h new file mode 100644 index 00000000..4058042f --- /dev/null +++ b/Easy2D-Audio/audio.h @@ -0,0 +1,66 @@ +// Copyright (c) 2016-2018 Easy2D - Nomango +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#pragma once +#include "Voice.h" + +namespace easy2d +{ + class E2D_API Audio + : public Singleton