diff --git a/projects/kiwano-audio/kiwano-audio.vcxproj b/projects/kiwano-audio/kiwano-audio.vcxproj
index 256809b1..f847e71e 100644
--- a/projects/kiwano-audio/kiwano-audio.vcxproj
+++ b/projects/kiwano-audio/kiwano-audio.vcxproj
@@ -1,9 +1,12 @@
-
+
+
+
+
@@ -27,11 +30,13 @@
-
+
+
+
+
-
{1B97937D-8184-426C-BE71-29A163DC76C9}
@@ -184,4 +189,4 @@
-
+
\ No newline at end of file
diff --git a/projects/kiwano-audio/kiwano-audio.vcxproj.filters b/projects/kiwano-audio/kiwano-audio.vcxproj.filters
index f3d328d0..6ee09c2d 100644
--- a/projects/kiwano-audio/kiwano-audio.vcxproj.filters
+++ b/projects/kiwano-audio/kiwano-audio.vcxproj.filters
@@ -5,14 +5,32 @@
-
+
+
+ MediaFoundation
+
+
+
+ MediaFoundation
+
-
-
+
+
+ MediaFoundation
+
+
+
+ MediaFoundation
+
+
+
+
+ {213a58dc-8054-4bc7-bac5-456e889ae038}
+
\ No newline at end of file
diff --git a/src/kiwano-audio/AudioData.cpp b/src/kiwano-audio/AudioData.cpp
new file mode 100644
index 00000000..d065acf8
--- /dev/null
+++ b/src/kiwano-audio/AudioData.cpp
@@ -0,0 +1,44 @@
+// Copyright (c) 2016-2018 Kiwano - 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
+
+namespace kiwano
+{
+namespace audio
+{
+AudioData::AudioData(const BinaryData& data, const AudioMeta& meta)
+ : data_(data)
+ , meta_(meta)
+{
+}
+
+AudioMeta AudioData::GetMeta() const
+{
+ return meta_;
+}
+
+BinaryData AudioData::GetData() const
+{
+ return data_;
+}
+
+} // namespace audio
+} // namespace kiwano
diff --git a/src/kiwano-audio/AudioData.h b/src/kiwano-audio/AudioData.h
new file mode 100644
index 00000000..f8c9c17c
--- /dev/null
+++ b/src/kiwano-audio/AudioData.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2016-2018 Kiwano - 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 kiwano
+{
+namespace audio
+{
+
+KGE_DECLARE_SMART_PTR(AudioData);
+
+/**
+ * \addtogroup Audio
+ * @{
+ */
+
+/**
+ * \~chinese
+ * @brief 音频格式
+ */
+enum class AudioFormat
+{
+ PCM,
+};
+
+/**
+ * \~chinese
+ * @brief 音频元数据
+ */
+struct AudioMeta
+{
+ AudioFormat format = AudioFormat::PCM; ///< 音频格式
+ uint16_t channels = 2; ///< 声道,单声道为1,立体声为2
+ uint32_t samples_per_sec = 44100; ///< 采样率,11025 表示 11.025kHz。PCM格式的采样率通常为44.1kHz
+ uint16_t bits_per_sample = 16; ///< 位深,PCM格式为 8 或 16
+ uint16_t block_align = 4; ///< 块对齐,PCM格式通常是 (channels * bits_per_sample) / 8
+};
+
+/**
+ * \~chinese
+ * @brief 音频数据
+ */
+class AudioData : public NativeObject
+{
+public:
+ /// \~chinese
+ /// @brief 音频数据
+ /// @param data 音频数据
+ /// @param meta 音频元数据
+ AudioData(const BinaryData& data, const AudioMeta& meta);
+
+ /// \~chinese
+ /// @brief 获取音频元数据
+ AudioMeta GetMeta() const;
+
+ /// \~chinese
+ /// @brief 获取数据
+ BinaryData GetData() const;
+
+protected:
+ AudioData() = default;
+
+protected:
+ BinaryData data_;
+ AudioMeta meta_;
+};
+
+/** @} */
+
+} // namespace audio
+} // namespace kiwano
diff --git a/src/kiwano-audio/AudioModule.cpp b/src/kiwano-audio/AudioModule.cpp
index 9298a569..77d112a3 100644
--- a/src/kiwano-audio/AudioModule.cpp
+++ b/src/kiwano-audio/AudioModule.cpp
@@ -17,11 +17,12 @@
// 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
-#include
#include
#include
+#include
+#include
+#include
+#include
namespace kiwano
{
@@ -89,16 +90,16 @@ void AudioModule::SetupModule()
{
KGE_DEBUG_LOGF("Creating audio resources");
- HRESULT hr = dlls::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL);
-
- if (SUCCEEDED(hr))
- {
- hr = dlls::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR);
- }
+ HRESULT hr = dlls::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR);
if (SUCCEEDED(hr))
{
hr = x_audio2_->CreateMasteringVoice(&mastering_voice_);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ RegisterTranscoder("*", MakePtr());
}
KGE_THROW_IF_FAILED(hr, "Create audio resources failed");
@@ -119,28 +120,28 @@ void AudioModule::DestroyModule()
x_audio2_->Release();
x_audio2_ = nullptr;
}
-
- dlls::MediaFoundation::Get().MFShutdown();
}
-bool AudioModule::CreateSound(Sound& sound, const AudioMetadata& metadata)
+bool AudioModule::CreateSound(Sound& sound, AudioDataPtr data)
{
KGE_ASSERT(x_audio2_ && "AudioModule hasn't been initialized!");
- HRESULT hr = S_OK;
-
- WAVEFORMATEX wave_format = { 0 };
- wave_format.wFormatTag = ConvertWaveFormat(metadata.format);
- wave_format.nChannels = WORD(metadata.channels);
- wave_format.nSamplesPerSec = DWORD(metadata.samples_per_sec);
- wave_format.wBitsPerSample = WORD(metadata.bits_per_sample);
- wave_format.nBlockAlign = WORD(metadata.block_align);
- wave_format.nAvgBytesPerSec = DWORD(metadata.samples_per_sec * metadata.block_align);
+ HRESULT hr = S_OK;
- WAVEFORMATEX* wave_format_ptr = &wave_format;
- if (metadata.extra_data != nullptr)
+ WAVEFORMATEX* wave_fmt = data->GetNative();
+ if (wave_fmt == nullptr)
{
- wave_format_ptr = reinterpret_cast(metadata.extra_data);
+ const auto meta = data->GetMeta();
+ WAVEFORMATEX tmp = { 0 };
+ tmp.wFormatTag = ConvertWaveFormat(meta.format);
+ tmp.nChannels = WORD(meta.channels);
+ tmp.nSamplesPerSec = DWORD(meta.samples_per_sec);
+ tmp.wBitsPerSample = WORD(meta.bits_per_sample);
+ tmp.nBlockAlign = WORD(meta.block_align);
+ tmp.nAvgBytesPerSec = DWORD(meta.samples_per_sec * meta.block_align);
+
+ data->SetNative(tmp);
+ wave_fmt = const_cast(data->GetNative().CastPtr());
}
if (SUCCEEDED(hr))
@@ -150,7 +151,7 @@ bool AudioModule::CreateSound(Sound& sound, const AudioMetadata& metadata)
auto callback = const_cast(chain->GetNative().CastPtr());
IXAudio2SourceVoice* voice = nullptr;
- hr = x_audio2_->CreateSourceVoice(&voice, wave_format_ptr, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback);
+ hr = x_audio2_->CreateSourceVoice(&voice, wave_fmt, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback);
if (SUCCEEDED(hr))
{
sound.Close();
@@ -179,6 +180,51 @@ void AudioModule::Close()
if (x_audio2_)
x_audio2_->StopEngine();
+}
+
+void AudioModule::RegisterTranscoder(const String& ext, TranscoderPtr transcoder)
+{
+ registered_transcoders_.insert(std::make_pair(ext, transcoder));
}
+
+TranscoderPtr AudioModule::GetTranscoder(const String& ext)
+{
+ auto iter = registered_transcoders_.find(ext);
+ if (iter != registered_transcoders_.end())
+ {
+ return iter->second;
+ }
+ return registered_transcoders_.at("*");
+}
+
+AudioDataPtr AudioModule::Decode(const String& file_path)
+{
+ if (!FileSystem::GetInstance().IsFileExists(file_path))
+ {
+ KGE_WARNF("Media file '%s' not found", file_path.c_str());
+ return nullptr;
+ }
+
+ const auto ext = FileSystem::GetInstance().GetFileExt(file_path);
+
+ auto transcoder = GetTranscoder(ext);
+ if (!transcoder)
+ {
+ return nullptr;
+ }
+ String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path);
+ return transcoder->Decode(full_path);
+}
+
+AudioDataPtr AudioModule::Decode(const Resource& res, const String& ext)
+{
+ auto transcoder = GetTranscoder(ext);
+ if (!transcoder)
+ {
+ return nullptr;
+ }
+ return transcoder->Decode(res);
+}
+
} // namespace audio
} // namespace kiwano
diff --git a/src/kiwano-audio/AudioModule.h b/src/kiwano-audio/AudioModule.h
index 54f36147..0d772922 100644
--- a/src/kiwano-audio/AudioModule.h
+++ b/src/kiwano-audio/AudioModule.h
@@ -59,9 +59,30 @@ public:
/// @brief 关闭音频设备
void Close();
+ /// \~chinese
+ /// @brief 注册解码器
+ /// @param ext 文件类型(如:ogg),* 为默认解码器
+ /// @param transcoder 解码器
+ void RegisterTranscoder(const String& ext, TranscoderPtr transcoder);
+
+ /// \~chinese
+ /// @brief 获取解码器
+ TranscoderPtr GetTranscoder(const String& ext);
+
+ /// \~chinese
+ /// @brief 解码音频
+ /// @param file_path 本地音频文件路径
+ AudioDataPtr Decode(const String& file_path);
+
+ /// \~chinese
+ /// @brief 解码音频
+ /// @param res 音频资源
+ /// @param ext 音频类型,决定了使用何种解码器
+ AudioDataPtr Decode(const Resource& res, const String& ext = "");
+
/// \~chinese
/// @brief 创建音频
- bool CreateSound(Sound& sound, const AudioMetadata& metadata);
+ bool CreateSound(Sound& sound, AudioDataPtr data);
public:
void SetupModule() override;
@@ -76,6 +97,8 @@ private:
private:
IXAudio2* x_audio2_;
IXAudio2MasteringVoice* mastering_voice_;
+
+ UnorderedMap registered_transcoders_;
};
/** @} */
diff --git a/src/kiwano-audio/Transcoder.cpp b/src/kiwano-audio/MediaFoundation/Transcoder.cpp
similarity index 67%
rename from src/kiwano-audio/Transcoder.cpp
rename to src/kiwano-audio/MediaFoundation/Transcoder.cpp
index db0218eb..b4f7b4bb 100644
--- a/src/kiwano-audio/Transcoder.cpp
+++ b/src/kiwano-audio/MediaFoundation/Transcoder.cpp
@@ -18,113 +18,53 @@
// 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
-#include
+#include
+#include
#include
-#include
#include
#include
#include
-#include
-#include
-#include
-#include
namespace kiwano
{
namespace audio
-{
-
-HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std::unique_ptr& output_data,
- uint32_t& output_size);
-
-Transcoder::Transcoder(const String& file_path)
{
- Load(file_path);
-}
-
-Transcoder::Transcoder(const Resource& res)
-{
- Load(res);
-}
-
-Transcoder::Transcoder(const BinaryData& data, const AudioMetadata& metadata)
-{
- Load(data, metadata);
-}
-Transcoder::~Transcoder()
+HRESULT ReadSource(AudioDataPtr& output, IMFSourceReader* reader);
+
+MFTranscoder::MFTranscoder()
{
- Clear();
-}
-
-AudioMetadata Transcoder::GetMetadata() const
-{
- return metadata_;
+ HRESULT hr = dlls::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL);
+ KGE_THROW_IF_FAILED(hr, "Initialize Media Foundation failed!");
}
-BinaryData Transcoder::GetData() const
+MFTranscoder::~MFTranscoder()
{
- return data_;
+ dlls::MediaFoundation::Get().MFShutdown();
}
-void Transcoder::Clear()
+AudioDataPtr MFTranscoder::Decode(const String& file_path)
{
- if (metadata_.extra_data != nullptr)
- {
- WAVEFORMATEX* wave_format = reinterpret_cast(metadata_.extra_data);
- ::CoTaskMemFree(wave_format);
- metadata_.extra_data = nullptr;
- }
-
- data_ = {};
- raw_.reset();
-}
+ ComPtr reader;
-bool Transcoder::Load(const BinaryData& data, const AudioMetadata& metadata)
-{
- data_ = data;
- metadata_ = metadata;
- return true;
-}
+ WideString path = strings::NarrowToWide(file_path);
+ HRESULT hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromURL(path.c_str(), nullptr, &reader);
-bool Transcoder::Load(const String& file_path)
-{
- if (!FileSystem::GetInstance().IsFileExists(file_path))
- {
- KGE_WARNF("Media file '%s' not found", file_path.c_str());
- return false;
- }
-
- String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path);
- WideString path = strings::NarrowToWide(full_path);
-
- ComPtr reader;
-
- HRESULT hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromURL(path.c_str(), nullptr, &reader);
+ AudioDataPtr output;
if (SUCCEEDED(hr))
{
- hr = ReadSource(reader.Get(), metadata_, raw_, data_.size);
-
- if (SUCCEEDED(hr))
- {
- data_.buffer = raw_.get();
- }
+ hr = ReadSource(output, reader.Get());
}
-
- if (FAILED(hr))
+
+ if (FAILED(hr))
{
Fail(strings::Format("%s failed (%#x): %s", __FUNCTION__, hr, "Load audio failed"));
- return false;
+ return nullptr;
}
- return true;
+ return output;
}
-bool Transcoder::Load(const Resource& res)
+AudioDataPtr MFTranscoder::Decode(const Resource& res)
{
HRESULT hr = S_OK;
@@ -136,7 +76,7 @@ bool Transcoder::Load(const Resource& res)
if (!data.IsValid())
{
Fail("invalid audio data");
- return false;
+ return nullptr;
}
stream = win32::dlls::Shlwapi::Get().SHCreateMemStream(static_cast(data.buffer),
@@ -145,7 +85,7 @@ bool Transcoder::Load(const Resource& res)
if (stream == nullptr)
{
Fail("SHCreateMemStream failed");
- return false;
+ return nullptr;
}
if (SUCCEEDED(hr))
@@ -158,26 +98,51 @@ bool Transcoder::Load(const Resource& res)
hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromByteStream(byte_stream.Get(), nullptr, &reader);
}
+ AudioDataPtr output;
if (SUCCEEDED(hr))
{
- hr = ReadSource(reader.Get(), metadata_, raw_, data_.size);
-
- if (SUCCEEDED(hr))
- {
- data_.buffer = raw_.get();
- }
+ hr = ReadSource(output, reader.Get());
}
if (FAILED(hr))
{
Fail(strings::Format("%s failed (%#x): %s", __FUNCTION__, hr, "Load audio failed"));
- return false;
+ return nullptr;
}
- return true;
-}
+ return output;
+}
-HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std::unique_ptr& output_data,
- uint32_t& output_size)
+class MFAudioData : public AudioData
+{
+public:
+ MFAudioData(WAVEFORMATEX* wave_fmt, std::unique_ptr raw, uint32_t size)
+ : wave_fmt_(wave_fmt)
+ , raw_(std::move(raw))
+ {
+ data_ = BinaryData{ raw_.get(), size };
+ meta_.format = AudioFormat::PCM;
+ meta_.channels = uint16_t(wave_fmt->nChannels);
+ meta_.samples_per_sec = uint32_t(wave_fmt->nSamplesPerSec);
+ meta_.bits_per_sample = uint16_t(wave_fmt->wBitsPerSample);
+ meta_.block_align = uint16_t(wave_fmt->nBlockAlign);
+
+ SetNative(wave_fmt);
+ }
+
+ virtual ~MFAudioData()
+ {
+ if (wave_fmt_ != nullptr)
+ {
+ ::CoTaskMemFree(wave_fmt_);
+ wave_fmt_ = nullptr;
+ }
+ }
+
+ WAVEFORMATEX* wave_fmt_;
+ std::unique_ptr raw_;
+};
+
+HRESULT ReadSource(AudioDataPtr& output, IMFSourceReader* reader)
{
HRESULT hr = S_OK;
@@ -224,16 +189,6 @@ HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std:
uncompressed_type.Get(), &wave_format, &size, (DWORD)MFWaveFormatExConvertFlag_Normal);
}
- if (SUCCEEDED(hr))
- {
- output_metadata.format = AudioFormat::PCM;
- output_metadata.channels = uint16_t(wave_format->nChannels);
- output_metadata.samples_per_sec = uint32_t(wave_format->nSamplesPerSec);
- output_metadata.bits_per_sample = uint16_t(wave_format->wBitsPerSample);
- output_metadata.block_align = uint16_t(wave_format->nBlockAlign);
- output_metadata.extra_data = wave_format;
- }
-
// 估算音频流大小
DWORD max_stream_size = 0;
if (SUCCEEDED(hr))
@@ -241,9 +196,9 @@ HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std:
PROPVARIANT prop;
PropVariantInit(&prop);
- hr = reader->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &prop);
-
- if (SUCCEEDED(hr))
+ hr = reader->GetPresentationAttribute((DWORD)MF_SOURCE_READER_MEDIASOURCE, MF_PD_DURATION, &prop);
+
+ if (SUCCEEDED(hr))
{
LONGLONG duration = prop.uhVal.QuadPart;
max_stream_size = static_cast((duration * wave_format->nAvgBytesPerSec) / 10000000 + 1);
@@ -255,9 +210,9 @@ HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std:
if (SUCCEEDED(hr))
{
DWORD flags = 0;
- DWORD position = 0;
-
- output_data = std::make_unique(max_stream_size);
+ DWORD position = 0;
+
+ auto output_data = std::make_unique(max_stream_size);
ComPtr sample;
ComPtr buffer;
@@ -319,12 +274,8 @@ HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std:
if (SUCCEEDED(hr))
{
- output_size = uint32_t(position);
- }
- else
- {
- output_data.reset();
- output_size = 0;
+ output = new MFAudioData(wave_format, std::move(output_data), uint32_t(position));
+ return hr;
}
}
}
diff --git a/src/kiwano-audio/MediaFoundation/Transcoder.h b/src/kiwano-audio/MediaFoundation/Transcoder.h
new file mode 100644
index 00000000..b8844d7f
--- /dev/null
+++ b/src/kiwano-audio/MediaFoundation/Transcoder.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2016-2018 Kiwano - 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 kiwano
+{
+namespace audio
+{
+
+/**
+ * \addtogroup Audio
+ * @{
+ */
+
+/**
+ * \~chinese
+ * @brief Media Foundation 音频解码器
+ * @detail 支持 .wav .mp3 等常见音频类型
+ */
+class KGE_API MFTranscoder : public Transcoder
+{
+public:
+ MFTranscoder();
+
+ virtual ~MFTranscoder();
+
+ AudioDataPtr Decode(const String& file_path) override;
+
+ AudioDataPtr Decode(const Resource& res) override;
+};
+
+/** @} */
+
+} // namespace audio
+} // namespace kiwano
diff --git a/src/kiwano-audio/MediaFoundation/mflib.cpp b/src/kiwano-audio/MediaFoundation/mflib.cpp
new file mode 100644
index 00000000..99e204b9
--- /dev/null
+++ b/src/kiwano-audio/MediaFoundation/mflib.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2016-2018 Kiwano - 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
+#include
+#include
+
+namespace kiwano
+{
+namespace audio
+{
+namespace dlls
+{
+MediaFoundation::MediaFoundation()
+ : mfplat()
+ , mfreadwrite()
+ , MFStartup(nullptr)
+ , MFShutdown(nullptr)
+ , MFCreateMediaType(nullptr)
+ , MFCreateWaveFormatExFromMFMediaType(nullptr)
+ , MFCreateSourceReaderFromURL(nullptr)
+ , MFCreateSourceReaderFromByteStream(nullptr)
+ , MFCreateMFByteStreamOnStream(nullptr)
+{
+ if (mfplat.Load("Mfplat.dll"))
+ {
+ MFStartup = mfplat.GetProcess("MFStartup");
+ MFShutdown = mfplat.GetProcess("MFShutdown");
+ MFCreateMediaType = mfplat.GetProcess("MFCreateMediaType");
+ MFCreateWaveFormatExFromMFMediaType =
+ mfplat.GetProcess("MFCreateWaveFormatExFromMFMediaType");
+ MFCreateMFByteStreamOnStream =
+ mfplat.GetProcess("MFCreateMFByteStreamOnStream");
+ }
+ else
+ {
+ KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load Mfplat.dll failed");
+ }
+
+ if (mfreadwrite.Load("Mfreadwrite.dll"))
+ {
+ MFCreateSourceReaderFromURL =
+ mfreadwrite.GetProcess("MFCreateSourceReaderFromURL");
+ MFCreateSourceReaderFromByteStream =
+ mfreadwrite.GetProcess("MFCreateSourceReaderFromByteStream");
+ }
+ else
+ {
+ KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load Mfreadwrite.dll failed");
+ }
+}
+} // namespace dlls
+} // namespace audio
+} // namespace kiwano
diff --git a/src/kiwano-audio/MediaFoundation/mflib.h b/src/kiwano-audio/MediaFoundation/mflib.h
new file mode 100644
index 00000000..ada1e1aa
--- /dev/null
+++ b/src/kiwano-audio/MediaFoundation/mflib.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2016-2018 Kiwano - 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
+
+#ifndef INITGUID
+#define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio
+#endif
+
+#include
+#include
+#include
+#include
+
+#ifndef KGE_DOXYGEN_DO_NOT_INCLUDE
+
+namespace kiwano
+{
+namespace audio
+{
+namespace dlls
+{
+class KGE_API MediaFoundation
+{
+public:
+ static inline MediaFoundation& Get()
+ {
+ static MediaFoundation instance;
+ return instance;
+ }
+
+ // 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**);
+
+ PFN_MFStartup MFStartup;
+ PFN_MFShutdown MFShutdown;
+ PFN_MFCreateMediaType MFCreateMediaType;
+ PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType;
+ PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL;
+ PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream;
+ PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream;
+
+private:
+ MediaFoundation();
+
+ MediaFoundation(const MediaFoundation&) = delete;
+ MediaFoundation& operator=(const MediaFoundation&) = delete;
+
+ Library mfplat;
+ Library mfreadwrite;
+};
+} // namespace dlls
+} // namespace audio
+} // namespace kiwano
+
+#endif
diff --git a/src/kiwano-audio/Sound.cpp b/src/kiwano-audio/Sound.cpp
index 3be889fe..8bbbdebf 100644
--- a/src/kiwano-audio/Sound.cpp
+++ b/src/kiwano-audio/Sound.cpp
@@ -1,418 +1,407 @@
-// Copyright (c) 2016-2018 Kiwano - 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
-#include
-#include
-
-namespace kiwano
-{
-namespace audio
-{
-
-Sound::Sound(const String& file_path)
- : Sound()
-{
- TranscoderPtr transcoder = MakePtr(file_path);
- Load(transcoder);
-}
-
-Sound::Sound(const Resource& res)
- : Sound()
-{
- TranscoderPtr transcoder = MakePtr(res);
- Load(transcoder);
-}
-
-Sound::Sound(const BinaryData& data, const AudioMetadata& metadata)
-{
- TranscoderPtr transcoder = MakePtr(data, metadata);
- Load(transcoder);
-}
-
-Sound::Sound(TranscoderPtr transcoder)
- : Sound()
-{
- Load(transcoder);
-}
-
-Sound::Sound()
- : opened_(false)
- , playing_(false)
- , volume_(1.f)
-{
-}
-
-Sound::~Sound()
-{
- Close();
-}
-
-bool Sound::Load(TranscoderPtr transcoder)
-{
- if (!transcoder->IsValid())
- {
- return false;
+// Copyright (c) 2016-2018 Kiwano - 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
+#include
+#include
+#include
+
+namespace kiwano
+{
+namespace audio
+{
+
+Sound::Sound(const String& file_path)
+ : Sound()
+{
+ Load(AudioModule::GetInstance().Decode(file_path));
+}
+
+Sound::Sound(const Resource& res, const String& ext)
+ : Sound()
+{
+ Load(AudioModule::GetInstance().Decode(res, ext));
+}
+
+Sound::Sound(AudioDataPtr data)
+ : Sound()
+{
+ Load(data);
+}
+
+Sound::Sound()
+ : opened_(false)
+ , playing_(false)
+ , volume_(1.f)
+{
+}
+
+Sound::~Sound()
+{
+ Close();
+}
+
+bool Sound::Load(AudioDataPtr data)
+{
+ if (!data)
+ {
+ return false;
}
-
- BinaryData data_;
- AudioMetadata metadata_;
-
- if (opened_)
- {
- Close();
- }
- if (!AudioModule::GetInstance().CreateSound(*this, transcoder->GetMetadata()))
- {
- return false;
- }
-
- // reset volume
- ResetVolume();
-
- coder_ = transcoder;
- opened_ = true;
- return true;
-}
-
-void Sound::Play(int loop_count)
-{
- if (!opened_)
- {
- KGE_ERRORF("Sound must be opened first!");
- return;
- }
-
- auto voice = GetNativePtr();
- KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
-
- // if sound stream is not empty, stop() will clear it
- XAUDIO2_VOICE_STATE state;
- voice->GetState(&state);
- if (state.BuffersQueued)
- Stop();
-
- // clamp loop count
- loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1);
-
- auto data = coder_->GetData();
-
- XAUDIO2_BUFFER xaudio2_buffer = { 0 };
- xaudio2_buffer.pAudioData = reinterpret_cast(data.buffer);
- xaudio2_buffer.Flags = XAUDIO2_END_OF_STREAM;
- xaudio2_buffer.AudioBytes = UINT32(data.size);
- xaudio2_buffer.LoopCount = static_cast(loop_count);
-
- HRESULT hr = voice->SubmitSourceBuffer(&xaudio2_buffer);
- if (SUCCEEDED(hr))
- {
- hr = voice->Start();
- }
-
- if (FAILED(hr))
- {
- KGE_ERRORF("Submitting source buffer failed with HRESULT of %08X", hr);
- }
-
- playing_ = SUCCEEDED(hr);
-}
-
-void Sound::Pause()
-{
- auto voice = GetNativePtr();
- KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
-
- HRESULT hr = voice->Stop();
- if (SUCCEEDED(hr))
- playing_ = false;
-
- if (FAILED(hr))
- {
- KGE_ERRORF("Pause voice failed with HRESULT of %08X", hr);
- }
-}
-
-void Sound::Resume()
-{
- auto voice = GetNativePtr();
- KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
-
- HRESULT hr = voice->Start();
- if (SUCCEEDED(hr))
- playing_ = true;
-
- if (FAILED(hr))
- {
- KGE_ERRORF("Start voice failed with HRESULT of %08X", hr);
- }
-}
-
-void Sound::Stop()
-{
- auto voice = GetNativePtr();
- KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
-
- HRESULT hr = voice->Stop();
-
- if (SUCCEEDED(hr))
- hr = voice->ExitLoop();
-
- if (SUCCEEDED(hr))
- hr = voice->FlushSourceBuffers();
-
- if (SUCCEEDED(hr))
- playing_ = false;
-
- if (FAILED(hr))
- {
- KGE_ERRORF("Stop voice failed with HRESULT of %08X", hr);
- }
-}
-
-void Sound::Close()
-{
- auto voice = GetNativePtr();
- if (voice)
- {
- voice->Stop();
- voice->FlushSourceBuffers();
- voice->DestroyVoice();
- }
-
- coder_ = nullptr;
- opened_ = false;
- playing_ = false;
-}
-
-bool Sound::IsPlaying() const
-{
- if (opened_)
- {
- if (!playing_)
- return false;
-
- auto voice = GetNativePtr();
- if (!voice)
- return false;
-
- XAUDIO2_VOICE_STATE state;
- voice->GetState(&state);
- return !!state.BuffersQueued;
- }
- return false;
-}
-
-float Sound::GetVolume() const
-{
- return volume_;
-}
-
-void Sound::SetVolume(float volume)
-{
- if (volume_ == volume)
- {
- return;
- }
- volume_ = volume;
-
- auto voice = GetNativePtr();
- if (voice)
- {
- float actual_volume = GetCallbackChain()->OnVolumeChanged(this, volume_);
- actual_volume = std::min(std::max(actual_volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL);
- voice->SetVolume(actual_volume);
- }
-}
-
-void Sound::ResetVolume()
-{
- const float old_volume = volume_;
-
- volume_ += 1.f;
- SetVolume(old_volume);
-}
-
-SoundCallbackPtr Sound::GetCallbackChain()
-{
- class SoundCallbackChain : public SoundCallback
- {
- public:
- Sound* sound = nullptr;
-
- void OnStart(Sound*) override
- {
- for (auto& cb : sound->GetCallbacks())
- {
- if (cb)
- {
- cb->OnStart(sound);
- }
- }
- RemoveUsedCallbacks();
- }
-
- void OnLoopEnd(Sound*) override
- {
- for (auto& cb : sound->GetCallbacks())
- {
- if (cb)
- {
- cb->OnLoopEnd(sound);
- }
- }
- RemoveUsedCallbacks();
- }
-
- void OnEnd(Sound*) override
- {
- for (auto& cb : sound->GetCallbacks())
- {
- if (cb)
- {
- cb->OnEnd(sound);
- }
- }
- RemoveUsedCallbacks();
- }
-
- float OnVolumeChanged(Sound*, float volume) override
- {
- float actual_volume = volume;
- for (auto& cb : sound->GetCallbacks())
- {
- if (cb)
- {
- actual_volume = cb->OnVolumeChanged(sound, volume);
- }
- }
- return actual_volume;
- }
-
- void RemoveUsedCallbacks()
- {
- auto& cbs = sound->GetCallbacks();
- auto iter = cbs.begin();
- while (iter != cbs.end())
- {
- if (*iter == nullptr)
- {
- iter = cbs.erase(iter);
- }
- else
- {
- iter++;
- }
- }
- }
- };
-
- if (!callback_chain_)
- {
- auto chain = MakePtr();
- chain->sound = this;
- callback_chain_ = chain;
- }
- return callback_chain_;
-}
-
-SoundCallbackPtr SoundCallback::OnStart(const Function& cb)
-{
- class SoundCallbackFunc : public SoundCallback
- {
- public:
- Function cb;
-
- void OnStart(Sound* sound) override
- {
- if (cb)
- {
- cb(sound);
- }
- }
- };
- auto ptr = MakePtr();
- ptr->cb = cb;
- return ptr;
-}
-
-SoundCallbackPtr SoundCallback::OnLoopEnd(const Function& cb)
-{
- class SoundCallbackFunc : public SoundCallback
- {
- public:
- Function cb;
-
- void OnLoopEnd(Sound* sound) override
- {
- if (cb)
- {
- cb(sound);
- }
- }
- };
- auto ptr = MakePtr();
- ptr->cb = cb;
- return ptr;
-}
-
-SoundCallbackPtr SoundCallback::OnEnd(const Function& cb)
-{
- class SoundCallbackFunc : public SoundCallback
- {
- public:
- Function cb;
-
- void OnEnd(Sound* sound) override
- {
- if (cb)
- {
- cb(sound);
- }
- }
- };
- auto ptr = MakePtr();
- ptr->cb = cb;
- return ptr;
-}
-
-SoundCallbackPtr SoundCallback::OnVolumeChanged(const Function& cb)
-{
- class SoundCallbackFunc : public SoundCallback
- {
- public:
- Function cb;
-
- float OnVolumeChanged(Sound* sound, float volume) override
- {
- if (cb)
- {
- return cb(sound, volume);
- }
- return volume;
- }
- };
- auto ptr = MakePtr();
- ptr->cb = cb;
- return ptr;
-}
-
-} // namespace audio
-} // namespace kiwano
+ if (opened_)
+ {
+ Close();
+ }
+ if (!AudioModule::GetInstance().CreateSound(*this, data))
+ {
+ return false;
+ }
+
+ // reset volume
+ ResetVolume();
+
+ data_ = data;
+ opened_ = true;
+ return true;
+}
+
+void Sound::Play(int loop_count)
+{
+ if (!opened_)
+ {
+ KGE_ERRORF("Sound must be opened first!");
+ return;
+ }
+
+ auto voice = GetNative();
+ KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
+
+ // if sound stream is not empty, stop() will clear it
+ XAUDIO2_VOICE_STATE state;
+ voice->GetState(&state);
+ if (state.BuffersQueued)
+ Stop();
+
+ // clamp loop count
+ loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1);
+
+ auto data = data_->GetData();
+
+ XAUDIO2_BUFFER xaudio2_buffer = { 0 };
+ xaudio2_buffer.pAudioData = reinterpret_cast(data.buffer);
+ xaudio2_buffer.Flags = XAUDIO2_END_OF_STREAM;
+ xaudio2_buffer.AudioBytes = UINT32(data.size);
+ xaudio2_buffer.LoopCount = static_cast(loop_count);
+
+ HRESULT hr = voice->SubmitSourceBuffer(&xaudio2_buffer);
+ if (SUCCEEDED(hr))
+ {
+ hr = voice->Start();
+ }
+
+ if (FAILED(hr))
+ {
+ KGE_ERRORF("Submitting source buffer failed with HRESULT of %08X", hr);
+ }
+
+ playing_ = SUCCEEDED(hr);
+}
+
+void Sound::Pause()
+{
+ auto voice = GetNative();
+ KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
+
+ HRESULT hr = voice->Stop();
+ if (SUCCEEDED(hr))
+ playing_ = false;
+
+ if (FAILED(hr))
+ {
+ KGE_ERRORF("Pause voice failed with HRESULT of %08X", hr);
+ }
+}
+
+void Sound::Resume()
+{
+ auto voice = GetNative();
+ KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
+
+ HRESULT hr = voice->Start();
+ if (SUCCEEDED(hr))
+ playing_ = true;
+
+ if (FAILED(hr))
+ {
+ KGE_ERRORF("Start voice failed with HRESULT of %08X", hr);
+ }
+}
+
+void Sound::Stop()
+{
+ auto voice = GetNative();
+ KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
+
+ HRESULT hr = voice->Stop();
+
+ if (SUCCEEDED(hr))
+ hr = voice->ExitLoop();
+
+ if (SUCCEEDED(hr))
+ hr = voice->FlushSourceBuffers();
+
+ if (SUCCEEDED(hr))
+ playing_ = false;
+
+ if (FAILED(hr))
+ {
+ KGE_ERRORF("Stop voice failed with HRESULT of %08X", hr);
+ }
+}
+
+void Sound::Close()
+{
+ auto voice = GetNative();
+ if (voice)
+ {
+ voice->Stop();
+ voice->FlushSourceBuffers();
+ voice->DestroyVoice();
+ }
+
+ data_ = nullptr;
+ opened_ = false;
+ playing_ = false;
+}
+
+bool Sound::IsPlaying() const
+{
+ if (opened_)
+ {
+ if (!playing_)
+ return false;
+
+ auto voice = GetNative();
+ if (!voice)
+ return false;
+
+ XAUDIO2_VOICE_STATE state;
+ voice->GetState(&state);
+ return !!state.BuffersQueued;
+ }
+ return false;
+}
+
+float Sound::GetVolume() const
+{
+ return volume_;
+}
+
+void Sound::SetVolume(float volume)
+{
+ if (volume_ == volume)
+ {
+ return;
+ }
+ volume_ = volume;
+
+ auto voice = GetNative();
+ if (voice)
+ {
+ float actual_volume = GetCallbackChain()->OnVolumeChanged(this, volume_);
+ actual_volume = std::min(std::max(actual_volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL);
+ voice->SetVolume(actual_volume);
+ }
+}
+
+void Sound::ResetVolume()
+{
+ const float old_volume = volume_;
+
+ volume_ += 1.f;
+ SetVolume(old_volume);
+}
+
+SoundCallbackPtr Sound::GetCallbackChain()
+{
+ class SoundCallbackChain : public SoundCallback
+ {
+ public:
+ Sound* sound = nullptr;
+
+ void OnStart(Sound*) override
+ {
+ for (auto& cb : sound->GetCallbacks())
+ {
+ if (cb)
+ {
+ cb->OnStart(sound);
+ }
+ }
+ RemoveUsedCallbacks();
+ }
+
+ void OnLoopEnd(Sound*) override
+ {
+ for (auto& cb : sound->GetCallbacks())
+ {
+ if (cb)
+ {
+ cb->OnLoopEnd(sound);
+ }
+ }
+ RemoveUsedCallbacks();
+ }
+
+ void OnEnd(Sound*) override
+ {
+ for (auto& cb : sound->GetCallbacks())
+ {
+ if (cb)
+ {
+ cb->OnEnd(sound);
+ }
+ }
+ RemoveUsedCallbacks();
+ }
+
+ float OnVolumeChanged(Sound*, float volume) override
+ {
+ float actual_volume = volume;
+ for (auto& cb : sound->GetCallbacks())
+ {
+ if (cb)
+ {
+ actual_volume = cb->OnVolumeChanged(sound, volume);
+ }
+ }
+ return actual_volume;
+ }
+
+ void RemoveUsedCallbacks()
+ {
+ auto& cbs = sound->GetCallbacks();
+ auto iter = cbs.begin();
+ while (iter != cbs.end())
+ {
+ if (*iter == nullptr)
+ {
+ iter = cbs.erase(iter);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+ }
+ };
+
+ if (!callback_chain_)
+ {
+ auto chain = MakePtr();
+ chain->sound = this;
+ callback_chain_ = chain;
+ }
+ return callback_chain_;
+}
+
+SoundCallbackPtr SoundCallback::OnStart(const Function& cb)
+{
+ class SoundCallbackFunc : public SoundCallback
+ {
+ public:
+ Function cb;
+
+ void OnStart(Sound* sound) override
+ {
+ if (cb)
+ {
+ cb(sound);
+ }
+ }
+ };
+ auto ptr = MakePtr();
+ ptr->cb = cb;
+ return ptr;
+}
+
+SoundCallbackPtr SoundCallback::OnLoopEnd(const Function& cb)
+{
+ class SoundCallbackFunc : public SoundCallback
+ {
+ public:
+ Function cb;
+
+ void OnLoopEnd(Sound* sound) override
+ {
+ if (cb)
+ {
+ cb(sound);
+ }
+ }
+ };
+ auto ptr = MakePtr();
+ ptr->cb = cb;
+ return ptr;
+}
+
+SoundCallbackPtr SoundCallback::OnEnd(const Function& cb)
+{
+ class SoundCallbackFunc : public SoundCallback
+ {
+ public:
+ Function cb;
+
+ void OnEnd(Sound* sound) override
+ {
+ if (cb)
+ {
+ cb(sound);
+ }
+ }
+ };
+ auto ptr = MakePtr();
+ ptr->cb = cb;
+ return ptr;
+}
+
+SoundCallbackPtr SoundCallback::OnVolumeChanged(const Function& cb)
+{
+ class SoundCallbackFunc : public SoundCallback
+ {
+ public:
+ Function cb;
+
+ float OnVolumeChanged(Sound* sound, float volume) override
+ {
+ if (cb)
+ {
+ return cb(sound, volume);
+ }
+ return volume;
+ }
+ };
+ auto ptr = MakePtr();
+ ptr->cb = cb;
+ return ptr;
+}
+
+} // namespace audio
+} // namespace kiwano
diff --git a/src/kiwano-audio/Sound.h b/src/kiwano-audio/Sound.h
index ba60a91b..8f17e315 100644
--- a/src/kiwano-audio/Sound.h
+++ b/src/kiwano-audio/Sound.h
@@ -1,204 +1,194 @@
-// Copyright (c) 2016-2018 Kiwano - 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 kiwano
-{
-namespace audio
-{
-class AudioModule;
-class SoundPlayer;
-
-KGE_DECLARE_SMART_PTR(Sound);
-KGE_DECLARE_SMART_PTR(SoundCallback);
-
-/**
- * \addtogroup Audio
- * @{
- */
-
-/**
- * \~chinese
- * @brief 音频回调
- */
-class KGE_API SoundCallback : public NativeObject
-{
-public:
- /// \~chinese
- /// @brief 创建一个回调,在音频开始播放时执行
- static SoundCallbackPtr OnStart(const Function& cb);
-
- /// \~chinese
- /// @brief 创建一个回调,在音频循环结束时执行
- static SoundCallbackPtr OnLoopEnd(const Function& cb);
-
- /// \~chinese
- /// @brief 创建一个回调,在音频结束时执行
- static SoundCallbackPtr OnEnd(const Function& cb);
-
- /// \~chinese
- /// @brief 创建一个回调,在音频修改音量时执行
- static SoundCallbackPtr OnVolumeChanged(const Function& cb);
-
- /// \~chinese
- /// @brief 在音频开始播放时执行
- virtual inline void OnStart(Sound* sound) {}
-
- /// \~chinese
- /// @brief 在音频循环结束时执行
- virtual inline void OnLoopEnd(Sound* sound) {}
-
- /// \~chinese
- /// @brief 在音频结束时执行
- virtual inline void OnEnd(Sound* sound) {}
-
- /// \~chinese
- /// @brief 在音频修改音量时执行
- virtual inline float OnVolumeChanged(Sound* sound, float volume)
- {
- return volume;
- }
-};
-
-/**
- * \~chinese
- * @brief 音频
- */
-class KGE_API Sound : public NativeObject
-{
- friend class AudioModule;
- friend class SoundPlayer;
-
-public:
- /// \~chinese
- /// @brief 创建音频对象
- /// @param res 本地音频文件路径
- Sound(const String& file_path);
-
- /// \~chinese
- /// @brief 创建音频对象
- /// @param res 音频资源
- Sound(const Resource& res);
+// Copyright (c) 2016-2018 Kiwano - 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.
- /// \~chinese
- /// @brief 创建音频对象
- /// @param data 音频数据
- /// @param metadata 音频元数据
- Sound(const BinaryData& data, const AudioMetadata& metadata);
-
- /// \~chinese
- /// @brief 创建音频对象
- /// @param transcoder 音频解码器
- Sound(TranscoderPtr transcoder);
-
- Sound();
-
- virtual ~Sound();
-
- /// \~chinese
- /// @brief 播放
- /// @param loop_count 播放循环次数,设置 -1 为循环播放
- void Play(int loop_count = 0);
-
- /// \~chinese
- /// @brief 暂停
- void Pause();
-
- /// \~chinese
- /// @brief 继续
- void Resume();
-
- /// \~chinese
- /// @brief 停止
- void Stop();
-
- /// \~chinese
- /// @brief 关闭并销毁资源
- void Close();
-
- /// \~chinese
- /// @brief 是否正在播放
- bool IsPlaying() const;
-
- /// \~chinese
- /// @brief 获取音量
- float GetVolume() const;
-
- /// \~chinese
- /// @brief 设置音量
- /// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量
- void SetVolume(float volume);
-
- /// \~chinese
- /// @brief 添加回调
- void AddCallback(SoundCallbackPtr callback);
-
- /// \~chinese
- /// @brief 获取所有回调
- List& GetCallbacks();
-
- /// \~chinese
- /// @brief 获取所有回调
- const List& GetCallbacks() const;
-
-protected:
- /// \~chinese
- /// @brief 加载音频
- /// @param transcoder 音频解码器
- bool Load(TranscoderPtr transcoder);
-
- SoundCallbackPtr GetCallbackChain();
-
- void ResetVolume();
-
-private:
- bool opened_;
- bool playing_;
- float volume_;
- TranscoderPtr coder_;
-
- SoundCallbackPtr callback_chain_;
- List callbacks_;
-};
-
-/** @} */
-
-inline List& kiwano::audio::Sound::GetCallbacks()
-{
- return callbacks_;
-}
-
-inline const List& kiwano::audio::Sound::GetCallbacks() const
-{
- return callbacks_;
-}
-
-inline void kiwano::audio::Sound::AddCallback(SoundCallbackPtr callback)
-{
- callbacks_.push_back(callback);
-}
-
-} // namespace audio
-} // namespace kiwano
+#pragma once
+#include
+#include
+
+namespace kiwano
+{
+namespace audio
+{
+class AudioModule;
+class SoundPlayer;
+
+KGE_DECLARE_SMART_PTR(Sound);
+KGE_DECLARE_SMART_PTR(SoundCallback);
+
+/**
+ * \addtogroup Audio
+ * @{
+ */
+
+/**
+ * \~chinese
+ * @brief 音频回调
+ */
+class KGE_API SoundCallback : public NativeObject
+{
+public:
+ /// \~chinese
+ /// @brief 创建一个回调,在音频开始播放时执行
+ static SoundCallbackPtr OnStart(const Function& cb);
+
+ /// \~chinese
+ /// @brief 创建一个回调,在音频循环结束时执行
+ static SoundCallbackPtr OnLoopEnd(const Function& cb);
+
+ /// \~chinese
+ /// @brief 创建一个回调,在音频结束时执行
+ static SoundCallbackPtr OnEnd(const Function& cb);
+
+ /// \~chinese
+ /// @brief 创建一个回调,在音频修改音量时执行
+ static SoundCallbackPtr OnVolumeChanged(const Function& cb);
+
+ /// \~chinese
+ /// @brief 在音频开始播放时执行
+ virtual inline void OnStart(Sound* sound) {}
+
+ /// \~chinese
+ /// @brief 在音频循环结束时执行
+ virtual inline void OnLoopEnd(Sound* sound) {}
+
+ /// \~chinese
+ /// @brief 在音频结束时执行
+ virtual inline void OnEnd(Sound* sound) {}
+
+ /// \~chinese
+ /// @brief 在音频修改音量时执行
+ virtual inline float OnVolumeChanged(Sound* sound, float volume)
+ {
+ return volume;
+ }
+};
+
+/**
+ * \~chinese
+ * @brief 音频
+ */
+class KGE_API Sound : public NativeObject
+{
+ friend class AudioModule;
+ friend class SoundPlayer;
+
+public:
+ /// \~chinese
+ /// @brief 创建音频对象
+ /// @param file_path 本地音频文件路径
+ Sound(const String& file_path);
+
+ /// \~chinese
+ /// @brief 创建音频对象
+ /// @param res 音频资源
+ /// @param ext 音频类型,决定了使用何种解码器
+ Sound(const Resource& res, const String& ext = "");
+
+ /// \~chinese
+ /// @brief 创建音频对象
+ /// @param data 音频数据
+ Sound(AudioDataPtr data);
+
+ Sound();
+
+ virtual ~Sound();
+
+ /// \~chinese
+ /// @brief 播放
+ /// @param loop_count 播放循环次数,设置 -1 为循环播放
+ void Play(int loop_count = 0);
+
+ /// \~chinese
+ /// @brief 暂停
+ void Pause();
+
+ /// \~chinese
+ /// @brief 继续
+ void Resume();
+
+ /// \~chinese
+ /// @brief 停止
+ void Stop();
+
+ /// \~chinese
+ /// @brief 关闭并销毁资源
+ void Close();
+
+ /// \~chinese
+ /// @brief 是否正在播放
+ bool IsPlaying() const;
+
+ /// \~chinese
+ /// @brief 获取音量
+ float GetVolume() const;
+
+ /// \~chinese
+ /// @brief 设置音量
+ /// @param volume 音量大小,1.0 为原始音量, 大于 1 为放大音量, 0 为最小音量
+ void SetVolume(float volume);
+
+ /// \~chinese
+ /// @brief 添加回调
+ void AddCallback(SoundCallbackPtr callback);
+
+ /// \~chinese
+ /// @brief 获取所有回调
+ List& GetCallbacks();
+
+ /// \~chinese
+ /// @brief 获取所有回调
+ const List& GetCallbacks() const;
+
+protected:
+ bool Load(AudioDataPtr data);
+
+ SoundCallbackPtr GetCallbackChain();
+
+ void ResetVolume();
+
+private:
+ bool opened_;
+ bool playing_;
+ float volume_;
+ AudioDataPtr data_;
+
+ SoundCallbackPtr callback_chain_;
+ List callbacks_;
+};
+
+/** @} */
+
+inline List& kiwano::audio::Sound::GetCallbacks()
+{
+ return callbacks_;
+}
+
+inline const List& kiwano::audio::Sound::GetCallbacks() const
+{
+ return callbacks_;
+}
+
+inline void kiwano::audio::Sound::AddCallback(SoundCallbackPtr callback)
+{
+ callbacks_.push_back(callback);
+}
+
+} // namespace audio
+} // namespace kiwano
diff --git a/src/kiwano-audio/SoundPlayer.cpp b/src/kiwano-audio/SoundPlayer.cpp
index aac8dce7..ab4883da 100644
--- a/src/kiwano-audio/SoundPlayer.cpp
+++ b/src/kiwano-audio/SoundPlayer.cpp
@@ -56,14 +56,14 @@ SoundPlayer::SoundPlayer()
SoundPlayer::~SoundPlayer()
{}
-TranscoderPtr SoundPlayer::Preload(const String& file_path)
+AudioDataPtr SoundPlayer::Preload(const String& file_path)
{
size_t hash_code = std::hash{}(file_path);
if (cache_.count(hash_code))
{
return cache_.at(hash_code);
}
- TranscoderPtr ptr = MakePtr(file_path);
+ AudioDataPtr ptr = AudioModule::GetInstance().Decode(file_path);
if (ptr)
{
cache_.insert(std::make_pair(hash_code, ptr));
@@ -71,14 +71,14 @@ TranscoderPtr SoundPlayer::Preload(const String& file_path)
return ptr;
}
-TranscoderPtr SoundPlayer::Preload(const Resource& res)
+AudioDataPtr SoundPlayer::Preload(const Resource& res, const String& ext)
{
size_t hash_code = res.GetId();
if (cache_.count(hash_code))
{
return cache_.at(hash_code);
}
- TranscoderPtr ptr = MakePtr(res);
+ AudioDataPtr ptr = AudioModule::GetInstance().Decode(res, ext);
if (ptr)
{
cache_.insert(std::make_pair(hash_code, ptr));
@@ -96,28 +96,16 @@ void SoundPlayer::Play(SoundPtr sound, int loop_count)
}
}
-SoundPtr SoundPlayer::Play(const String& file_path, int loop_count, std::initializer_list callbacks)
+SoundPtr SoundPlayer::Play(const String& file_path, int loop_count)
{
- TranscoderPtr transcoder = Preload(file_path);
-
- SoundPtr sound = new Sound(transcoder);
- for (const auto& cb : callbacks)
- {
- sound->AddCallback(cb);
- }
+ SoundPtr sound = new Sound(Preload(file_path));
Play(sound, loop_count);
return sound;
}
-SoundPtr SoundPlayer::Play(const Resource& res, int loop_count, std::initializer_list callbacks)
+SoundPtr SoundPlayer::Play(const Resource& res, int loop_count)
{
- TranscoderPtr transcoder = Preload(res);
-
- SoundPtr sound = new Sound(transcoder);
- for (const auto& cb : callbacks)
- {
- sound->AddCallback(cb);
- }
+ SoundPtr sound = new Sound(Preload(res));
Play(sound, loop_count);
return sound;
}
diff --git a/src/kiwano-audio/SoundPlayer.h b/src/kiwano-audio/SoundPlayer.h
index bc8ee10e..d5a9c204 100644
--- a/src/kiwano-audio/SoundPlayer.h
+++ b/src/kiwano-audio/SoundPlayer.h
@@ -48,11 +48,11 @@ public:
/// \~chinese
/// @brief 预加载音频
/// @details
- TranscoderPtr Preload(const String& file_path);
+ AudioDataPtr Preload(const String& file_path);
/// \~chinese
/// @brief 预加载音频资源
- TranscoderPtr Preload(const Resource& res);
+ AudioDataPtr Preload(const Resource& res, const String& ext = "");
/// \~chinese
/// @brief 播放音频
@@ -64,15 +64,13 @@ public:
/// @brief 播放音频
/// @param file_path 本地音频文件路径
/// @param loop_count 播放循环次数,设置 -1 为循环播放
- /// @param callbacks 注册回调
- SoundPtr Play(const String& file_path, int loop_count = 0, std::initializer_list callbacks = {});
+ SoundPtr Play(const String& file_path, int loop_count = 0);
/// \~chinese
/// @brief 播放音频
/// @param res 音频资源
/// @param loop_count 播放循环次数,设置 -1 为循环播放
- /// @param callbacks 注册回调
- SoundPtr Play(const Resource& res, int loop_count = 0, std::initializer_list callbacks = {});
+ SoundPtr Play(const Resource& res, int loop_count = 0);
/// \~chinese
/// @brief 暂停所有音频
@@ -120,7 +118,7 @@ protected:
SoundList trash_;
SoundCallbackPtr callback_;
- UnorderedMap cache_;
+ UnorderedMap cache_;
};
/** @} */
diff --git a/src/kiwano-audio/Transcoder.h b/src/kiwano-audio/Transcoder.h
index 9909c7ba..c0df6c13 100644
--- a/src/kiwano-audio/Transcoder.h
+++ b/src/kiwano-audio/Transcoder.h
@@ -1,110 +1,56 @@
-// Copyright (c) 2016-2018 Kiwano - 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
-
-namespace kiwano
-{
-namespace audio
-{
-class AudioModule;
-
-KGE_DECLARE_SMART_PTR(Transcoder);
-
-/**
- * \addtogroup Audio
- * @{
+// Copyright (c) 2016-2018 Kiwano - 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
+
+namespace kiwano
+{
+namespace audio
+{
+
+KGE_DECLARE_SMART_PTR(Transcoder);
+
+/**
+ * \addtogroup Audio
+ * @{
*/
-/**
- * \~chinese
- * @brief 音频格式
- */
-enum class AudioFormat
+/**
+ * \~chinese
+ * @brief 音频解码器
+ */
+class KGE_API Transcoder : public ObjectBase
{
- PCM,
+public:
+ Transcoder() = default;
+
+ virtual ~Transcoder() = default;
+
+ virtual AudioDataPtr Decode(const String& file_path) = 0;
+
+ virtual AudioDataPtr Decode(const Resource& res) = 0;
};
-/**
- * \~chinese
- * @brief 音频元数据
- */
-struct AudioMetadata
-{
- AudioFormat format = AudioFormat::PCM; ///< 音频格式
- uint16_t channels = 2; ///< 声道,单声道为1,立体声为2
- uint32_t samples_per_sec = 44100; ///< 采样率,11025 表示 11.025kHz。PCM格式的采样率通常为44.1kHz
- uint16_t bits_per_sample = 16; ///< 位深,PCM格式为 8 或 16
- uint16_t block_align = 4; ///< 块对齐,PCM格式通常是 (channels * bits_per_sample) / 8
- void* extra_data = nullptr; ///< 额外数据,不要设置这个字段
-};
-
-/**
- * \~chinese
- * @brief 音频解码器
- */
-class KGE_API Transcoder : public ObjectBase
-{
- friend class AudioModule;
-
-public:
- Transcoder(const String& file_path);
+/** @} */
- Transcoder(const Resource& res);
-
- Transcoder(const BinaryData& data, const AudioMetadata& metadata);
-
- virtual ~Transcoder();
-
- /// \~chinese
- /// @brief 获取音频元数据
- AudioMetadata GetMetadata() const;
-
- /// \~chinese
- /// @brief 获取数据
- BinaryData GetData() const;
-
- /// \~chinese
- /// @brief 清空数据
- void Clear();
-
-private:
- /// \~chinese
- /// @brief 解码本地音频文件
- bool Load(const String& file_path);
-
- /// \~chinese
- /// @brief 解码音频资源
- bool Load(const Resource& res);
-
- bool Load(const BinaryData& data, const AudioMetadata& metadata);
-
-private:
- std::unique_ptr raw_;
- BinaryData data_;
- AudioMetadata metadata_;
-};
-
-/** @} */
-
-} // namespace audio
-} // namespace kiwano
+} // namespace audio
+} // namespace kiwano
diff --git a/src/kiwano-audio/libraries.cpp b/src/kiwano-audio/libraries.cpp
index dc954079..52998d80 100644
--- a/src/kiwano-audio/libraries.cpp
+++ b/src/kiwano-audio/libraries.cpp
@@ -55,45 +55,6 @@ XAudio2::XAudio2()
KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load xaudio2.dll failed");
}
}
-
-MediaFoundation::MediaFoundation()
- : mfplat()
- , mfreadwrite()
- , MFStartup(nullptr)
- , MFShutdown(nullptr)
- , MFCreateMediaType(nullptr)
- , MFCreateWaveFormatExFromMFMediaType(nullptr)
- , MFCreateSourceReaderFromURL(nullptr)
- , MFCreateSourceReaderFromByteStream(nullptr)
- , MFCreateMFByteStreamOnStream(nullptr)
-{
- if (mfplat.Load("Mfplat.dll"))
- {
- MFStartup = mfplat.GetProcess("MFStartup");
- MFShutdown = mfplat.GetProcess("MFShutdown");
- MFCreateMediaType = mfplat.GetProcess("MFCreateMediaType");
- MFCreateWaveFormatExFromMFMediaType =
- mfplat.GetProcess("MFCreateWaveFormatExFromMFMediaType");
- MFCreateMFByteStreamOnStream =
- mfplat.GetProcess("MFCreateMFByteStreamOnStream");
- }
- else
- {
- KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load Mfplat.dll failed");
- }
-
- if (mfreadwrite.Load("Mfreadwrite.dll"))
- {
- MFCreateSourceReaderFromURL =
- mfreadwrite.GetProcess("MFCreateSourceReaderFromURL");
- MFCreateSourceReaderFromByteStream =
- mfreadwrite.GetProcess("MFCreateSourceReaderFromByteStream");
- }
- else
- {
- KGE_THROW_SYSTEM_ERROR(HRESULT_FROM_WIN32(GetLastError()), "Load Mfreadwrite.dll failed");
- }
-}
} // namespace dlls
} // namespace audio
} // namespace kiwano
diff --git a/src/kiwano-audio/libraries.h b/src/kiwano-audio/libraries.h
index 35324db0..316bd067 100644
--- a/src/kiwano-audio/libraries.h
+++ b/src/kiwano-audio/libraries.h
@@ -20,9 +20,6 @@
#pragma once
#include
-#include
-#include
-#include
#include
#ifndef KGE_DOXYGEN_DO_NOT_INCLUDE
@@ -55,42 +52,6 @@ private:
Library xaudio2;
};
-
-class KGE_API MediaFoundation
-{
-public:
- static inline MediaFoundation& Get()
- {
- static MediaFoundation instance;
- return instance;
- }
-
- // 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**);
-
- PFN_MFStartup MFStartup;
- PFN_MFShutdown MFShutdown;
- PFN_MFCreateMediaType MFCreateMediaType;
- PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType;
- PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL;
- PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream;
- PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream;
-
-private:
- MediaFoundation();
-
- MediaFoundation(const MediaFoundation&) = delete;
- MediaFoundation& operator=(const MediaFoundation&) = delete;
-
- Library mfplat;
- Library mfreadwrite;
-};
} // namespace dlls
} // namespace audio
} // namespace kiwano
diff --git a/src/kiwano/platform/FileSystem.cpp b/src/kiwano/platform/FileSystem.cpp
index 6ff5239c..3da8277c 100644
--- a/src/kiwano/platform/FileSystem.cpp
+++ b/src/kiwano/platform/FileSystem.cpp
@@ -130,6 +130,16 @@ String FileSystem::GetFullPathForFile(const String& file) const
return "";
}
+String FileSystem::GetFileExt(const String& file) const
+{
+ const auto pos = file.find_last_of(".");
+ if (pos == String::npos)
+ {
+ return "";
+ }
+ return file.substr(pos + 1);
+}
+
void FileSystem::AddFileLookupRule(const String& key, const String& file_path)
{
file_lookup_dict_.emplace(key, ConvertPathFormat(file_path));
diff --git a/src/kiwano/platform/FileSystem.h b/src/kiwano/platform/FileSystem.h
index a012ff78..794e7ca0 100644
--- a/src/kiwano/platform/FileSystem.h
+++ b/src/kiwano/platform/FileSystem.h
@@ -54,6 +54,14 @@ public:
*/
String GetFullPathForFile(const String& file) const;
+ /**
+ * \~chinese
+ * @brief 获取文件拓展名
+ * @param file 文件路径
+ * @return 文件拓展名,不包含 . 符号
+ */
+ String GetFileExt(const String& file) const;
+
/**
* \~chinese
* @brief 添加文件路径查找字典规则
diff --git a/src/kiwano/platform/NativeObject.hpp b/src/kiwano/platform/NativeObject.hpp
index 6f3d77ec..0b382739 100644
--- a/src/kiwano/platform/NativeObject.hpp
+++ b/src/kiwano/platform/NativeObject.hpp
@@ -40,9 +40,6 @@ public:
template
_Ty GetNative() const;
- template
- _Ty* GetNativePtr() const;
-
void SetNative(const Any& native);
void ResetNative();
@@ -65,17 +62,7 @@ inline _Ty NativeObject::GetNative() const
{
return native_.Cast<_Ty>();
}
- return nullptr;
-}
-
-template
-inline _Ty* NativeObject::GetNativePtr() const
-{
- if (native_.HasValue())
- {
- return native_.Cast<_Ty*>();
- }
- return nullptr;
+ return _Ty{};
}
inline void NativeObject::SetNative(const Any& native)