[deploy] feat(audio): support raw data sound
This commit is contained in:
parent
8ffd29eb38
commit
f861f3a5ab
|
|
@ -22,7 +22,6 @@
|
|||
#include <kiwano-audio/libraries.h>
|
||||
#include <kiwano/core/Exception.h>
|
||||
#include <kiwano/utils/Logger.h>
|
||||
#include <kiwano/platform/FileSystem.h>
|
||||
|
||||
namespace kiwano
|
||||
{
|
||||
|
|
@ -68,6 +67,16 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
WORD ConvertWaveFormat(AudioFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case kiwano::audio::AudioFormat::PCM:
|
||||
return WAVE_FORMAT_PCM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
AudioModule::AudioModule()
|
||||
: x_audio2_(nullptr)
|
||||
, mastering_voice_(nullptr)
|
||||
|
|
@ -114,47 +123,25 @@ void AudioModule::DestroyModule()
|
|||
dlls::MediaFoundation::Get().MFShutdown();
|
||||
}
|
||||
|
||||
TranscoderPtr AudioModule::CreateTranscoder(const String& file_path)
|
||||
{
|
||||
if (!FileSystem::GetInstance().IsFileExists(file_path))
|
||||
{
|
||||
KGE_WARNF("Media file '%s' not found", file_path.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path);
|
||||
|
||||
auto ptr = MakePtr<Transcoder>();
|
||||
HRESULT hr = ptr->LoadMediaFile(full_path);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
KGE_ERRORF("Load media file failed with HRESULT of %08X", hr);
|
||||
return nullptr;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
TranscoderPtr AudioModule::CreateTranscoder(const Resource& res)
|
||||
{
|
||||
auto ptr = MakePtr<Transcoder>();
|
||||
HRESULT hr = ptr->LoadMediaResource(res);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
KGE_ERRORF("Load media resource failed with HRESULT of %08X", hr);
|
||||
return nullptr;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bool AudioModule::CreateSound(Sound& sound, TranscoderPtr transcoder)
|
||||
bool AudioModule::CreateSound(Sound& sound, const AudioMetadata& metadata)
|
||||
{
|
||||
KGE_ASSERT(x_audio2_ && "AudioModule hasn't been initialized!");
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
auto buffer = transcoder->GetBuffer();
|
||||
if (buffer.format == nullptr)
|
||||
hr = E_INVALIDARG;
|
||||
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);
|
||||
|
||||
WAVEFORMATEX* wave_format_ptr = &wave_format;
|
||||
if (metadata.extra_data != nullptr)
|
||||
{
|
||||
wave_format_ptr = reinterpret_cast<WAVEFORMATEX*>(metadata.extra_data);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
|
@ -163,7 +150,7 @@ bool AudioModule::CreateSound(Sound& sound, TranscoderPtr transcoder)
|
|||
auto callback = const_cast<VoiceCallback*>(chain->GetNative().CastPtr<VoiceCallback>());
|
||||
|
||||
IXAudio2SourceVoice* voice = nullptr;
|
||||
hr = x_audio2_->CreateSourceVoice(&voice, buffer.format, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback);
|
||||
hr = x_audio2_->CreateSourceVoice(&voice, wave_format_ptr, 0, XAUDIO2_DEFAULT_FREQ_RATIO, callback);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
sound.Close();
|
||||
|
|
|
|||
|
|
@ -59,17 +59,9 @@ public:
|
|||
/// @brief 关闭音频设备
|
||||
void Close();
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建音频解码器
|
||||
TranscoderPtr CreateTranscoder(const String& file_path);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建音频解码器
|
||||
TranscoderPtr CreateTranscoder(const Resource& res);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建音频
|
||||
bool CreateSound(Sound& sound, TranscoderPtr transcoder);
|
||||
bool CreateSound(Sound& sound, const AudioMetadata& metadata);
|
||||
|
||||
public:
|
||||
void SetupModule() override;
|
||||
|
|
|
|||
|
|
@ -30,13 +30,21 @@ namespace audio
|
|||
Sound::Sound(const String& file_path)
|
||||
: Sound()
|
||||
{
|
||||
Load(file_path);
|
||||
TranscoderPtr transcoder = MakePtr<Transcoder>(file_path);
|
||||
Load(transcoder);
|
||||
}
|
||||
|
||||
Sound::Sound(const Resource& res)
|
||||
: Sound()
|
||||
{
|
||||
Load(res);
|
||||
TranscoderPtr transcoder = MakePtr<Transcoder>(res);
|
||||
Load(transcoder);
|
||||
}
|
||||
|
||||
Sound::Sound(const BinaryData& data, const AudioMetadata& metadata)
|
||||
{
|
||||
TranscoderPtr transcoder = MakePtr<Transcoder>(data, metadata);
|
||||
Load(transcoder);
|
||||
}
|
||||
|
||||
Sound::Sound(TranscoderPtr transcoder)
|
||||
|
|
@ -57,43 +65,22 @@ Sound::~Sound()
|
|||
Close();
|
||||
}
|
||||
|
||||
bool Sound::Load(const String& file_path)
|
||||
{
|
||||
if (opened_)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
TranscoderPtr transcoder = AudioModule::GetInstance().CreateTranscoder(file_path);
|
||||
if (!transcoder)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Load(transcoder);
|
||||
}
|
||||
|
||||
bool Sound::Load(const Resource& res)
|
||||
{
|
||||
if (opened_)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
TranscoderPtr transcoder = AudioModule::GetInstance().CreateTranscoder(res);
|
||||
if (!transcoder)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Load(transcoder);
|
||||
}
|
||||
|
||||
bool Sound::Load(TranscoderPtr transcoder)
|
||||
{
|
||||
if (!transcoder->IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
BinaryData data_;
|
||||
AudioMetadata metadata_;
|
||||
|
||||
if (opened_)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
if (!AudioModule::GetInstance().CreateSound(*this, transcoder))
|
||||
if (!AudioModule::GetInstance().CreateSound(*this, transcoder->GetMetadata()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -126,12 +113,12 @@ void Sound::Play(int loop_count)
|
|||
// clamp loop count
|
||||
loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1);
|
||||
|
||||
auto buffer = coder_->GetBuffer();
|
||||
auto data = coder_->GetData();
|
||||
|
||||
XAUDIO2_BUFFER xaudio2_buffer = { 0 };
|
||||
xaudio2_buffer.pAudioData = buffer.data;
|
||||
xaudio2_buffer.pAudioData = reinterpret_cast<BYTE*>(data.buffer);
|
||||
xaudio2_buffer.Flags = XAUDIO2_END_OF_STREAM;
|
||||
xaudio2_buffer.AudioBytes = buffer.size;
|
||||
xaudio2_buffer.AudioBytes = UINT32(data.size);
|
||||
xaudio2_buffer.LoopCount = static_cast<uint32_t>(loop_count);
|
||||
|
||||
HRESULT hr = voice->SubmitSourceBuffer(&xaudio2_buffer);
|
||||
|
|
@ -144,6 +131,10 @@ void Sound::Play(int loop_count)
|
|||
{
|
||||
KGE_ERRORF("Submitting source buffer failed with HRESULT of %08X", hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
KGE_LOG("success!!");
|
||||
}
|
||||
|
||||
playing_ = SUCCEEDED(hr);
|
||||
}
|
||||
|
|
@ -246,13 +237,13 @@ void Sound::SetVolume(float volume)
|
|||
}
|
||||
volume_ = volume;
|
||||
|
||||
float actual_volume = GetCallbackChain()->OnVolumeChanged(this, volume_);
|
||||
|
||||
auto voice = GetNativePtr<IXAudio2SourceVoice>();
|
||||
KGE_ASSERT(voice != nullptr && "IXAudio2SourceVoice* is NULL");
|
||||
|
||||
actual_volume = std::min(std::max(actual_volume, -XAUDIO2_MAX_VOLUME_LEVEL), XAUDIO2_MAX_VOLUME_LEVEL);
|
||||
voice->SetVolume(actual_volume);
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -101,6 +101,12 @@ public:
|
|||
/// @brief 创建音频对象
|
||||
/// @param res 音频资源
|
||||
Sound(const Resource& res);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建音频对象
|
||||
/// @param data 音频数据
|
||||
/// @param metadata 音频元数据
|
||||
Sound(const BinaryData& data, const AudioMetadata& metadata);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 创建音频对象
|
||||
|
|
@ -158,16 +164,6 @@ public:
|
|||
const List<SoundCallbackPtr>& GetCallbacks() const;
|
||||
|
||||
protected:
|
||||
/// \~chinese
|
||||
/// @brief 加载本地音频文件
|
||||
/// @param res 本地音频文件路径
|
||||
bool Load(const String& file_path);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 加载音频资源
|
||||
/// @param res 音频资源
|
||||
bool Load(const Resource& res);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 加载音频
|
||||
/// @param transcoder 音频解码器
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ TranscoderPtr SoundPlayer::Preload(const String& file_path)
|
|||
{
|
||||
return cache_.at(hash_code);
|
||||
}
|
||||
TranscoderPtr ptr = AudioModule::GetInstance().CreateTranscoder(file_path);
|
||||
TranscoderPtr ptr = MakePtr<Transcoder>(file_path);
|
||||
if (ptr)
|
||||
{
|
||||
cache_.insert(std::make_pair(hash_code, ptr));
|
||||
|
|
@ -78,7 +78,7 @@ TranscoderPtr SoundPlayer::Preload(const Resource& res)
|
|||
{
|
||||
return cache_.at(hash_code);
|
||||
}
|
||||
TranscoderPtr ptr = AudioModule::GetInstance().CreateTranscoder(res);
|
||||
TranscoderPtr ptr = MakePtr<Transcoder>(res);
|
||||
if (ptr)
|
||||
{
|
||||
cache_.insert(std::make_pair(hash_code, ptr));
|
||||
|
|
|
|||
|
|
@ -29,64 +29,102 @@
|
|||
#include <kiwano/utils/Logger.h>
|
||||
#include <kiwano/platform/win32/ComPtr.hpp>
|
||||
#include <kiwano/platform/win32/libraries.h>
|
||||
#include <kiwano/platform/FileSystem.h>
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
#include <mfreadwrite.h>
|
||||
|
||||
namespace kiwano
|
||||
{
|
||||
namespace audio
|
||||
{
|
||||
|
||||
HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std::unique_ptr<uint8_t[]>& output_data,
|
||||
uint32_t& output_size);
|
||||
|
||||
Transcoder::Transcoder(const String& file_path)
|
||||
{
|
||||
|
||||
Transcoder::Transcoder()
|
||||
: wave_format_(nullptr)
|
||||
, wave_data_(nullptr)
|
||||
, wave_size_(0)
|
||||
Load(file_path);
|
||||
}
|
||||
|
||||
Transcoder::Transcoder(const Resource& res)
|
||||
{
|
||||
Load(res);
|
||||
}
|
||||
|
||||
Transcoder::Transcoder(const BinaryData& data, const AudioMetadata& metadata)
|
||||
{
|
||||
Load(data, metadata);
|
||||
}
|
||||
|
||||
Transcoder::~Transcoder()
|
||||
{
|
||||
ClearBuffer();
|
||||
Clear();
|
||||
}
|
||||
|
||||
AudioMetadata Transcoder::GetMetadata() const
|
||||
{
|
||||
return metadata_;
|
||||
}
|
||||
|
||||
Transcoder::Buffer Transcoder::GetBuffer() const
|
||||
BinaryData Transcoder::GetData() const
|
||||
{
|
||||
return Buffer{ wave_data_, wave_size_, wave_format_ };
|
||||
return data_;
|
||||
}
|
||||
|
||||
void Transcoder::ClearBuffer()
|
||||
void Transcoder::Clear()
|
||||
{
|
||||
if (wave_format_)
|
||||
if (metadata_.extra_data != nullptr)
|
||||
{
|
||||
::CoTaskMemFree(wave_format_);
|
||||
wave_format_ = nullptr;
|
||||
WAVEFORMATEX* wave_format = reinterpret_cast<WAVEFORMATEX*>(metadata_.extra_data);
|
||||
::CoTaskMemFree(wave_format);
|
||||
metadata_.extra_data = nullptr;
|
||||
}
|
||||
|
||||
data_ = {};
|
||||
raw_.reset();
|
||||
}
|
||||
|
||||
bool Transcoder::Load(const BinaryData& data, const AudioMetadata& metadata)
|
||||
{
|
||||
data_ = data;
|
||||
metadata_ = metadata;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (wave_data_)
|
||||
{
|
||||
delete[] wave_data_;
|
||||
wave_data_ = nullptr;
|
||||
}
|
||||
String full_path = FileSystem::GetInstance().GetFullPathForFile(file_path);
|
||||
WideString path = strings::NarrowToWide(full_path);
|
||||
|
||||
wave_size_ = 0;
|
||||
}
|
||||
|
||||
HRESULT Transcoder::LoadMediaFile(const String& file_path)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
WideString path = strings::NarrowToWide(file_path);
|
||||
|
||||
ComPtr<IMFSourceReader> reader;
|
||||
hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromURL(path.c_str(), nullptr, &reader);
|
||||
ComPtr<IMFSourceReader> reader;
|
||||
|
||||
HRESULT hr = dlls::MediaFoundation::Get().MFCreateSourceReaderFromURL(path.c_str(), nullptr, &reader);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ReadSource(reader.Get());
|
||||
}
|
||||
hr = ReadSource(reader.Get(), metadata_, raw_, data_.size);
|
||||
|
||||
return hr;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
data_.buffer = raw_.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Fail(strings::Format("%s failed (%#x): %s", __FUNCTION__, hr, "Load audio failed"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
HRESULT Transcoder::LoadMediaResource(const Resource& res)
|
||||
bool Transcoder::Load(const Resource& res)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
|
|
@ -97,7 +135,8 @@ HRESULT Transcoder::LoadMediaResource(const Resource& res)
|
|||
BinaryData data = res.GetData();
|
||||
if (!data.IsValid())
|
||||
{
|
||||
return E_FAIL;
|
||||
Fail("invalid audio data");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream = win32::dlls::Shlwapi::Get().SHCreateMemStream(static_cast<const BYTE*>(data.buffer),
|
||||
|
|
@ -105,8 +144,8 @@ HRESULT Transcoder::LoadMediaResource(const Resource& res)
|
|||
|
||||
if (stream == nullptr)
|
||||
{
|
||||
KGE_ERRORF("SHCreateMemStream failed");
|
||||
return E_OUTOFMEMORY;
|
||||
Fail("SHCreateMemStream failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
|
|
@ -121,16 +160,26 @@ HRESULT Transcoder::LoadMediaResource(const Resource& res)
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = ReadSource(reader.Get());
|
||||
hr = ReadSource(reader.Get(), metadata_, raw_, data_.size);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
data_.buffer = raw_.get();
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Fail(strings::Format("%s failed (%#x): %s", __FUNCTION__, hr, "Load audio failed"));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
HRESULT Transcoder::ReadSource(IMFSourceReader* reader)
|
||||
HRESULT ReadSource(IMFSourceReader* reader, AudioMetadata& output_metadata, std::unique_ptr<uint8_t[]>& output_data,
|
||||
uint32_t& output_size)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
DWORD max_stream_size = 0;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
ComPtr<IMFMediaType> partial_type;
|
||||
ComPtr<IMFMediaType> uncompressed_type;
|
||||
|
|
@ -166,23 +215,39 @@ HRESULT Transcoder::ReadSource(IMFSourceReader* reader)
|
|||
}
|
||||
|
||||
// 获取 WAVEFORMAT 数据
|
||||
WAVEFORMATEX* wave_format = nullptr;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
uint32_t size = 0;
|
||||
hr = dlls::MediaFoundation::Get().MFCreateWaveFormatExFromMFMediaType(
|
||||
uncompressed_type.Get(), &wave_format_, &size, (DWORD)MFWaveFormatExConvertFlag_Normal);
|
||||
|
||||
hr = dlls::MediaFoundation::Get().MFCreateWaveFormatExFromMFMediaType(
|
||||
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))
|
||||
{
|
||||
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<DWORD>((duration * wave_format_->nAvgBytesPerSec) / 10000000 + 1);
|
||||
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<DWORD>((duration * wave_format->nAvgBytesPerSec) / 10000000 + 1);
|
||||
}
|
||||
PropVariantClear(&prop);
|
||||
}
|
||||
|
||||
|
|
@ -190,13 +255,14 @@ HRESULT Transcoder::ReadSource(IMFSourceReader* reader)
|
|||
if (SUCCEEDED(hr))
|
||||
{
|
||||
DWORD flags = 0;
|
||||
DWORD position = 0;
|
||||
BYTE* data = new (std::nothrow) BYTE[max_stream_size];
|
||||
DWORD position = 0;
|
||||
|
||||
output_data = std::make_unique<uint8_t[]>(max_stream_size);
|
||||
|
||||
ComPtr<IMFSample> sample;
|
||||
ComPtr<IMFMediaBuffer> buffer;
|
||||
|
||||
if (data == nullptr)
|
||||
if (output_data == nullptr)
|
||||
{
|
||||
KGE_ERRORF("Low memory");
|
||||
hr = E_OUTOFMEMORY;
|
||||
|
|
@ -236,7 +302,7 @@ HRESULT Transcoder::ReadSource(IMFSourceReader* reader)
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
::memcpy(data + position, audio_data, sample_buffer_length);
|
||||
::memcpy(output_data.get() + position, audio_data, sample_buffer_length);
|
||||
position += sample_buffer_length;
|
||||
hr = buffer->Unlock();
|
||||
}
|
||||
|
|
@ -253,17 +319,15 @@ HRESULT Transcoder::ReadSource(IMFSourceReader* reader)
|
|||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wave_data_ = data;
|
||||
wave_size_ = position;
|
||||
output_size = uint32_t(position);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete[] data;
|
||||
data = nullptr;
|
||||
output_data.reset();
|
||||
output_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
} // namespace audio
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@
|
|||
#pragma once
|
||||
#include <kiwano/core/Resource.h>
|
||||
#include <kiwano/base/ObjectBase.h>
|
||||
#include <mfapi.h>
|
||||
#include <mfidl.h>
|
||||
#include <mfreadwrite.h>
|
||||
|
||||
namespace kiwano
|
||||
{
|
||||
|
|
@ -36,7 +33,30 @@ KGE_DECLARE_SMART_PTR(Transcoder);
|
|||
/**
|
||||
* \addtogroup Audio
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \~chinese
|
||||
* @brief 音频格式
|
||||
*/
|
||||
enum class AudioFormat
|
||||
{
|
||||
PCM,
|
||||
};
|
||||
|
||||
/**
|
||||
* \~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
|
||||
|
|
@ -47,46 +67,41 @@ class KGE_API Transcoder : public ObjectBase
|
|||
friend class AudioModule;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \~chinese
|
||||
* @brief 音频数据缓冲
|
||||
*/
|
||||
struct Buffer
|
||||
{
|
||||
BYTE* data; ///< 音频数据
|
||||
uint32_t size; ///< 音频数据大小
|
||||
const WAVEFORMATEX* format; ///< 音频数据格式
|
||||
};
|
||||
Transcoder(const String& file_path);
|
||||
|
||||
Transcoder(const Resource& res);
|
||||
|
||||
Transcoder(const BinaryData& data, const AudioMetadata& metadata);
|
||||
|
||||
Transcoder();
|
||||
|
||||
~Transcoder();
|
||||
virtual ~Transcoder();
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 获取数据缓冲
|
||||
Buffer GetBuffer() const;
|
||||
/// @brief 获取音频元数据
|
||||
AudioMetadata GetMetadata() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 清空数据缓冲
|
||||
void ClearBuffer();
|
||||
/// @brief 获取数据
|
||||
BinaryData GetData() const;
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 清空数据
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
/// \~chinese
|
||||
/// @brief 解码本地音频文件
|
||||
HRESULT LoadMediaFile(const String& file_path);
|
||||
bool Load(const String& file_path);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 解码音频资源
|
||||
HRESULT LoadMediaResource(const Resource& res);
|
||||
|
||||
/// \~chinese
|
||||
/// @brief 读取音频源数据
|
||||
HRESULT ReadSource(IMFSourceReader* reader);
|
||||
bool Load(const Resource& res);
|
||||
|
||||
bool Load(const BinaryData& data, const AudioMetadata& metadata);
|
||||
|
||||
private:
|
||||
BYTE* wave_data_;
|
||||
uint32_t wave_size_;
|
||||
WAVEFORMATEX* wave_format_;
|
||||
std::unique_ptr<uint8_t[]> raw_;
|
||||
BinaryData data_;
|
||||
AudioMetadata metadata_;
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
|
|
|||
Loading…
Reference in New Issue