audio: switch waveout backend to pull rendering
This commit is contained in:
@@ -9,6 +9,13 @@ namespace Audio {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_lock<std::recursive_mutex> LockAudioState() {
|
||||
if (AudioSystem* audioSystem = AudioSystem::TryGetExisting()) {
|
||||
return audioSystem->AcquireStateLock();
|
||||
}
|
||||
return std::unique_lock<std::recursive_mutex>();
|
||||
}
|
||||
|
||||
AudioChannel ResolveChannel(uint32 channelIndex) {
|
||||
switch (channelIndex) {
|
||||
case 0: return AudioChannel::FrontLeft;
|
||||
@@ -27,31 +34,39 @@ AudioChannel ResolveChannel(uint32 channelIndex) {
|
||||
|
||||
AudioMixer::AudioMixer()
|
||||
{
|
||||
AudioSystem::Get().RegisterMixer(this);
|
||||
if (AudioSystem* audioSystem = AudioSystem::TryGetExisting()) {
|
||||
audioSystem->RegisterMixer(this);
|
||||
}
|
||||
for (int i = 0; i < static_cast<int>(AudioChannel::SideRight) + 1; ++i) {
|
||||
m_channelVolumes[static_cast<AudioChannel>(i)] = ChannelVolume{1.0f, false};
|
||||
}
|
||||
}
|
||||
|
||||
AudioMixer::~AudioMixer() {
|
||||
AudioSystem::Get().UnregisterMixer(this);
|
||||
if (AudioSystem* audioSystem = AudioSystem::TryGetExisting()) {
|
||||
audioSystem->UnregisterMixer(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::SetVolume(float volume) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_volume = std::max(0.0f, std::min(1.0f, volume));
|
||||
}
|
||||
|
||||
void AudioMixer::SetMute(bool mute) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_mute = mute;
|
||||
}
|
||||
|
||||
void AudioMixer::AddEffect(IAudioEffect* effect) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (effect && std::find(m_effects.begin(), m_effects.end(), effect) == m_effects.end()) {
|
||||
m_effects.push_back(effect);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::RemoveEffect(IAudioEffect* effect) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (!effect) return;
|
||||
auto it = std::find(m_effects.begin(), m_effects.end(), effect);
|
||||
if (it != m_effects.end()) {
|
||||
@@ -60,10 +75,12 @@ void AudioMixer::RemoveEffect(IAudioEffect* effect) {
|
||||
}
|
||||
|
||||
void AudioMixer::ClearEffects() {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_effects.clear();
|
||||
}
|
||||
|
||||
void AudioMixer::ProcessAudio(float* buffer, uint32 frameCount, uint32 channels, uint32 sampleRate) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (!buffer || frameCount == 0 || channels == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -98,14 +115,17 @@ void AudioMixer::ProcessAudio(float* buffer, uint32 frameCount, uint32 channels,
|
||||
}
|
||||
|
||||
void AudioMixer::SetOutputMixer(AudioMixer* mixer) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_outputMixer = mixer;
|
||||
}
|
||||
|
||||
void AudioMixer::Set3DParams(const Audio3DParams& params) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_3DParams = params;
|
||||
}
|
||||
|
||||
void AudioMixer::SetChannelVolume(AudioChannel channel, float volume) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
auto it = m_channelVolumes.find(channel);
|
||||
if (it != m_channelVolumes.end()) {
|
||||
it->second.volume = std::max(0.0f, std::min(1.0f, volume));
|
||||
@@ -113,6 +133,7 @@ void AudioMixer::SetChannelVolume(AudioChannel channel, float volume) {
|
||||
}
|
||||
|
||||
float AudioMixer::GetChannelVolume(AudioChannel channel) const {
|
||||
auto audioStateLock = LockAudioState();
|
||||
auto it = m_channelVolumes.find(channel);
|
||||
if (it != m_channelVolumes.end()) {
|
||||
return it->second.volume;
|
||||
|
||||
@@ -12,6 +12,8 @@ namespace Audio {
|
||||
|
||||
namespace {
|
||||
|
||||
AudioSystem* g_audioSystemInstance = nullptr;
|
||||
|
||||
void MixBufferInto(const std::vector<float>& source, std::vector<float>& destination) {
|
||||
if (destination.size() < source.size()) {
|
||||
destination.resize(source.size(), 0.0f);
|
||||
@@ -93,79 +95,128 @@ AudioSystem& AudioSystem::Get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
void AudioSystem::Initialize(const AudioConfig& config) {
|
||||
if (m_backend) {
|
||||
Shutdown();
|
||||
}
|
||||
AudioSystem* AudioSystem::TryGetExisting() {
|
||||
return g_audioSystemInstance;
|
||||
}
|
||||
|
||||
m_backend = std::make_unique<Audio::WaveOut::WaveOutBackend>();
|
||||
if (m_backend->Initialize(config)) {
|
||||
m_backend->Start();
|
||||
AudioSystem::AudioSystem() {
|
||||
g_audioSystemInstance = this;
|
||||
RegisterMixer(&m_masterMixer);
|
||||
}
|
||||
|
||||
AudioSystem::~AudioSystem() {
|
||||
g_audioSystemInstance = nullptr;
|
||||
}
|
||||
|
||||
void AudioSystem::Initialize(const AudioConfig& config) {
|
||||
Shutdown();
|
||||
|
||||
auto backend = std::make_unique<Audio::WaveOut::WaveOutBackend>();
|
||||
backend->SetRenderCallback(
|
||||
[this](float* buffer, uint32 frameCount, uint32 channels, uint32 sampleRate) {
|
||||
RenderAudio(buffer, frameCount, channels, sampleRate);
|
||||
});
|
||||
|
||||
if (backend->Initialize(config)) {
|
||||
IAudioBackend* backendPtr = backend.get();
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_backend = std::move(backend);
|
||||
}
|
||||
backendPtr->Start();
|
||||
std::cout << "AudioSystem initialized successfully" << std::endl;
|
||||
} else {
|
||||
std::cout << "Failed to initialize AudioSystem" << std::endl;
|
||||
m_backend.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSystem::Shutdown() {
|
||||
if (m_backend) {
|
||||
m_backend->Stop();
|
||||
m_backend->Shutdown();
|
||||
m_backend.reset();
|
||||
std::unique_ptr<IAudioBackend> backend;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
backend = std::move(m_backend);
|
||||
}
|
||||
|
||||
if (backend) {
|
||||
if (backend->IsRunning()) {
|
||||
backend->Stop();
|
||||
}
|
||||
backend->Shutdown();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_registeredMixers.clear();
|
||||
m_registeredSourceComponents.clear();
|
||||
m_registeredListenerComponents.clear();
|
||||
m_activeSources.clear();
|
||||
m_listenerReverbMixer = nullptr;
|
||||
m_listenerReverbLevel = 1.0f;
|
||||
m_listenerDopplerLevel = 1.0f;
|
||||
m_speedOfSound = 343.0f;
|
||||
m_listenerPosition = Math::Vector3::Zero();
|
||||
m_listenerRotation = Math::Quaternion::Identity();
|
||||
m_listenerVelocity = Math::Vector3::Zero();
|
||||
m_activeSourceSnapshot.clear();
|
||||
m_mixScratchBuffer.clear();
|
||||
m_sourceScratchBuffer.clear();
|
||||
m_mixerScratchBuffers.clear();
|
||||
m_mixerScratchChildren.clear();
|
||||
m_mixerScratchActiveMixers.clear();
|
||||
m_mixerScratchVisiting.clear();
|
||||
m_mixerScratchRendered.clear();
|
||||
m_deltaTime = 0.0f;
|
||||
m_stats = {};
|
||||
}
|
||||
m_registeredMixers.clear();
|
||||
m_registeredSourceComponents.clear();
|
||||
m_registeredListenerComponents.clear();
|
||||
m_activeSources.clear();
|
||||
m_listenerReverbMixer = nullptr;
|
||||
m_listenerReverbLevel = 1.0f;
|
||||
m_listenerDopplerLevel = 1.0f;
|
||||
m_speedOfSound = 343.0f;
|
||||
m_listenerPosition = Math::Vector3::Zero();
|
||||
m_listenerRotation = Math::Quaternion::Identity();
|
||||
m_listenerVelocity = Math::Vector3::Zero();
|
||||
m_activeSourceSnapshot.clear();
|
||||
m_mixScratchBuffer.clear();
|
||||
m_sourceScratchBuffer.clear();
|
||||
m_mixerScratchBuffers.clear();
|
||||
m_mixerScratchChildren.clear();
|
||||
m_mixerScratchActiveMixers.clear();
|
||||
m_mixerScratchVisiting.clear();
|
||||
m_mixerScratchRendered.clear();
|
||||
}
|
||||
|
||||
void AudioSystem::Update(float deltaTime) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_deltaTime = deltaTime;
|
||||
|
||||
if (!m_backend || !m_backend->IsRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& config = m_backend->GetConfig();
|
||||
const uint32 frameCount = config.bufferSize;
|
||||
RenderAudioBlock(frameCount, config.channels, config.sampleRate);
|
||||
m_backend->ProcessAudio(
|
||||
m_mixScratchBuffer.data(),
|
||||
frameCount,
|
||||
config.channels,
|
||||
config.sampleRate);
|
||||
|
||||
uint32 activeCount = 0;
|
||||
for (auto* source : m_activeSources) {
|
||||
if (source && source->IsPlaying()) {
|
||||
activeCount++;
|
||||
}
|
||||
if (!m_backend->UsesPullModel()) {
|
||||
const auto& config = m_backend->GetConfig();
|
||||
const uint32 frameCount = config.bufferSize;
|
||||
RenderAudioBlock(frameCount, config.channels, config.sampleRate);
|
||||
m_backend->ProcessAudio(
|
||||
m_mixScratchBuffer.data(),
|
||||
frameCount,
|
||||
config.channels,
|
||||
config.sampleRate);
|
||||
} else {
|
||||
UpdateStats();
|
||||
}
|
||||
m_stats.activeSources = activeCount;
|
||||
m_stats.totalSources = static_cast<uint32>(m_activeSources.size());
|
||||
}
|
||||
|
||||
void AudioSystem::SetBackend(std::unique_ptr<IAudioBackend> backend) {
|
||||
m_backend = std::move(backend);
|
||||
std::unique_ptr<IAudioBackend> previousBackend;
|
||||
if (backend) {
|
||||
backend->SetRenderCallback(
|
||||
[this](float* buffer, uint32 frameCount, uint32 channels, uint32 sampleRate) {
|
||||
RenderAudio(buffer, frameCount, channels, sampleRate);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
previousBackend = std::move(m_backend);
|
||||
m_backend = std::move(backend);
|
||||
}
|
||||
|
||||
if (previousBackend) {
|
||||
if (previousBackend->IsRunning()) {
|
||||
previousBackend->Stop();
|
||||
}
|
||||
previousBackend->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
std::string AudioSystem::GetCurrentDevice() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (m_backend) {
|
||||
return m_backend->GetDeviceName();
|
||||
}
|
||||
@@ -173,50 +224,61 @@ std::string AudioSystem::GetCurrentDevice() const {
|
||||
}
|
||||
|
||||
void AudioSystem::SetDevice(const std::string& deviceName) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (m_backend) {
|
||||
m_backend->SetDevice(deviceName);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSystem::GetAvailableDevices(std::vector<std::string>& devices) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (m_backend) {
|
||||
m_backend->GetAvailableDevices(devices);
|
||||
}
|
||||
}
|
||||
|
||||
float AudioSystem::GetMasterVolume() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
return m_masterMixer.GetVolume();
|
||||
}
|
||||
|
||||
void AudioSystem::SetMasterVolume(float volume) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_masterMixer.SetVolume(volume);
|
||||
}
|
||||
|
||||
bool AudioSystem::IsMuted() const {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
return m_masterMixer.IsMute();
|
||||
}
|
||||
|
||||
void AudioSystem::SetMuted(bool muted) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_masterMixer.SetMute(muted);
|
||||
}
|
||||
|
||||
void AudioSystem::SetListenerReverbMixer(AudioMixer* mixer) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_listenerReverbMixer = mixer;
|
||||
}
|
||||
|
||||
void AudioSystem::SetListenerReverbLevel(float level) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_listenerReverbLevel = std::clamp(level, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
void AudioSystem::SetListenerDopplerLevel(float level) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_listenerDopplerLevel = std::clamp(level, 0.0f, 5.0f);
|
||||
}
|
||||
|
||||
void AudioSystem::SetSpeedOfSound(float metersPerSecond) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_speedOfSound = std::max(1.0f, metersPerSecond);
|
||||
}
|
||||
|
||||
void AudioSystem::RenderAudio(float* buffer, uint32 frameCount, uint32 channels, uint32 sampleRate) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (buffer == nullptr || frameCount == 0 || channels == 0 || sampleRate == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -227,6 +289,7 @@ void AudioSystem::RenderAudio(float* buffer, uint32 frameCount, uint32 channels,
|
||||
}
|
||||
|
||||
void AudioSystem::ProcessAudio(float* buffer, uint32 frameCount, uint32 channels) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (m_backend) {
|
||||
const uint32 sampleRate =
|
||||
m_backend->GetConfig().sampleRate != 0 ? m_backend->GetConfig().sampleRate : 48000;
|
||||
@@ -235,15 +298,18 @@ void AudioSystem::ProcessAudio(float* buffer, uint32 frameCount, uint32 channels
|
||||
}
|
||||
|
||||
void AudioSystem::SetListenerTransform(const Math::Vector3& position, const Math::Quaternion& rotation) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_listenerPosition = position;
|
||||
m_listenerRotation = rotation;
|
||||
}
|
||||
|
||||
void AudioSystem::SetListenerVelocity(const Math::Vector3& velocity) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
m_listenerVelocity = velocity;
|
||||
}
|
||||
|
||||
void AudioSystem::RegisterSource(Components::AudioSourceComponent* source) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
@@ -254,6 +320,7 @@ void AudioSystem::RegisterSource(Components::AudioSourceComponent* source) {
|
||||
}
|
||||
|
||||
void AudioSystem::RegisterSourceComponent(Components::AudioSourceComponent* source) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
@@ -265,6 +332,7 @@ void AudioSystem::RegisterSourceComponent(Components::AudioSourceComponent* sour
|
||||
}
|
||||
|
||||
void AudioSystem::UnregisterSourceComponent(Components::AudioSourceComponent* source) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
@@ -276,6 +344,7 @@ void AudioSystem::UnregisterSourceComponent(Components::AudioSourceComponent* so
|
||||
}
|
||||
|
||||
void AudioSystem::RegisterListenerComponent(Components::AudioListenerComponent* listener) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
@@ -287,6 +356,7 @@ void AudioSystem::RegisterListenerComponent(Components::AudioListenerComponent*
|
||||
}
|
||||
|
||||
void AudioSystem::UnregisterListenerComponent(Components::AudioListenerComponent* listener) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!listener) {
|
||||
return;
|
||||
}
|
||||
@@ -297,6 +367,7 @@ void AudioSystem::UnregisterListenerComponent(Components::AudioListenerComponent
|
||||
}
|
||||
|
||||
void AudioSystem::RegisterMixer(AudioMixer* mixer) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!mixer) {
|
||||
return;
|
||||
}
|
||||
@@ -307,6 +378,7 @@ void AudioSystem::RegisterMixer(AudioMixer* mixer) {
|
||||
}
|
||||
|
||||
void AudioSystem::UnregisterMixer(AudioMixer* mixer) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!mixer) {
|
||||
return;
|
||||
}
|
||||
@@ -318,6 +390,7 @@ void AudioSystem::UnregisterMixer(AudioMixer* mixer) {
|
||||
}
|
||||
|
||||
void AudioSystem::UnregisterSource(Components::AudioSourceComponent* source) {
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
@@ -377,8 +450,7 @@ void AudioSystem::RenderAudioBlock(uint32 frameCount, uint32 channels, uint32 sa
|
||||
if (sampleCount == 0 || sampleRate == 0) {
|
||||
m_mixScratchBuffer.clear();
|
||||
m_sourceScratchBuffer.clear();
|
||||
m_stats.activeSources = 0;
|
||||
m_stats.totalSources = static_cast<uint32>(m_activeSources.size());
|
||||
UpdateStats();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -481,6 +553,10 @@ void AudioSystem::RenderAudioBlock(uint32 frameCount, uint32 channels, uint32 sa
|
||||
channels,
|
||||
sampleRate);
|
||||
|
||||
UpdateStats();
|
||||
}
|
||||
|
||||
void AudioSystem::UpdateStats() {
|
||||
uint32 activeCount = 0;
|
||||
for (auto* source : m_activeSources) {
|
||||
if (source && source->IsPlaying()) {
|
||||
|
||||
@@ -64,8 +64,7 @@ bool WaveOutBackend::Initialize(const AudioConfig& config) {
|
||||
const size_t bufferSampleCount = static_cast<size_t>(config.bufferSize) * config.channels;
|
||||
m_audioBuffer1.assign(bufferSampleCount, 0);
|
||||
m_audioBuffer2.assign(bufferSampleCount, 0);
|
||||
m_pendingMixBuffer.assign(bufferSampleCount, 0.0f);
|
||||
m_hasPendingMix = false;
|
||||
m_renderBuffer.assign(bufferSampleCount, 0.0f);
|
||||
m_buffer1Available = true;
|
||||
m_buffer2Available = true;
|
||||
|
||||
@@ -152,7 +151,7 @@ void WaveOutBackend::Start() {
|
||||
|
||||
void WaveOutBackend::Stop() {
|
||||
m_isRunning = false;
|
||||
m_dataReadyCond.notify_all();
|
||||
m_bufferReadyCond.notify_all();
|
||||
|
||||
if (m_hWaveOut != nullptr) {
|
||||
waveOutReset(m_hWaveOut);
|
||||
@@ -180,20 +179,15 @@ void WaveOutBackend::Resume() {
|
||||
|
||||
void WaveOutBackend::ProcessAudio(float* buffer, uint32 frameCount,
|
||||
uint32 channels, uint32 sampleRate) {
|
||||
(void)buffer;
|
||||
(void)frameCount;
|
||||
(void)channels;
|
||||
(void)sampleRate;
|
||||
}
|
||||
|
||||
if (buffer == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32 sampleCount = frameCount * channels;
|
||||
void WaveOutBackend::SetRenderCallback(RenderCallback callback) {
|
||||
std::lock_guard<std::mutex> lock(m_bufferMutex);
|
||||
m_pendingMixBuffer.assign(m_pendingMixBuffer.size(), 0.0f);
|
||||
for (uint32 i = 0; i < sampleCount && i < m_pendingMixBuffer.size(); ++i) {
|
||||
m_pendingMixBuffer[i] = buffer[i];
|
||||
}
|
||||
m_hasPendingMix = true;
|
||||
m_dataReadyCond.notify_one();
|
||||
m_renderCallback = std::move(callback);
|
||||
}
|
||||
|
||||
MMRESULT WaveOutBackend::InitDevice() {
|
||||
@@ -269,9 +263,8 @@ DWORD WINAPI WaveOutBackend::AudioThreadProc(LPVOID lpParameter) {
|
||||
void WaveOutBackend::AudioThread() {
|
||||
while (m_isRunning.load()) {
|
||||
std::unique_lock<std::mutex> lock(m_bufferMutex);
|
||||
m_dataReadyCond.wait(lock, [this] {
|
||||
return !m_isRunning.load() ||
|
||||
(m_hasPendingMix && (m_buffer1Available || m_buffer2Available));
|
||||
m_bufferReadyCond.wait(lock, [this] {
|
||||
return !m_isRunning.load() || m_buffer1Available || m_buffer2Available;
|
||||
});
|
||||
|
||||
if (!m_isRunning.load()) {
|
||||
@@ -293,10 +286,31 @@ void WaveOutBackend::AudioThread() {
|
||||
continue;
|
||||
}
|
||||
|
||||
FillPcm16Buffer(*targetBuffer, m_pendingMixBuffer);
|
||||
m_hasPendingMix = false;
|
||||
RenderCallback renderCallback;
|
||||
const uint32 frameCount = m_config.bufferSize;
|
||||
const uint32 channels = m_config.channels;
|
||||
const uint32 sampleRate = m_config.sampleRate;
|
||||
const size_t sampleCount = static_cast<size_t>(frameCount) * channels;
|
||||
|
||||
if (targetBuffer->size() != sampleCount) {
|
||||
targetBuffer->assign(sampleCount, 0);
|
||||
} else {
|
||||
std::fill(targetBuffer->begin(), targetBuffer->end(), 0);
|
||||
}
|
||||
|
||||
if (m_renderBuffer.size() != sampleCount) {
|
||||
m_renderBuffer.assign(sampleCount, 0.0f);
|
||||
} else {
|
||||
std::fill(m_renderBuffer.begin(), m_renderBuffer.end(), 0.0f);
|
||||
}
|
||||
renderCallback = m_renderCallback;
|
||||
lock.unlock();
|
||||
|
||||
if (renderCallback && !m_renderBuffer.empty()) {
|
||||
renderCallback(m_renderBuffer.data(), frameCount, channels, sampleRate);
|
||||
}
|
||||
FillPcm16Buffer(*targetBuffer, m_renderBuffer);
|
||||
|
||||
if (SubmitBuffer(*targetHeader, *targetBuffer) != MMSYSERR_NOERROR) {
|
||||
std::lock_guard<std::mutex> restoreLock(m_bufferMutex);
|
||||
if (targetHeader == &m_waveHeader1) {
|
||||
@@ -304,6 +318,7 @@ void WaveOutBackend::AudioThread() {
|
||||
} else {
|
||||
m_buffer2Available = true;
|
||||
}
|
||||
m_bufferReadyCond.notify_one();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -322,7 +337,7 @@ void WaveOutBackend::OnAudioCallback(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstan
|
||||
} else if (completedHeader == &m_waveHeader2) {
|
||||
m_buffer2Available = true;
|
||||
}
|
||||
m_dataReadyCond.notify_one();
|
||||
m_bufferReadyCond.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,17 @@
|
||||
namespace XCEngine {
|
||||
namespace Components {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_lock<std::recursive_mutex> LockAudioState() {
|
||||
if (Audio::AudioSystem* audioSystem = Audio::AudioSystem::TryGetExisting()) {
|
||||
return audioSystem->AcquireStateLock();
|
||||
}
|
||||
return std::unique_lock<std::recursive_mutex>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AudioListenerComponent::AudioListenerComponent()
|
||||
{
|
||||
Audio::AudioSystem::Get().RegisterListenerComponent(this);
|
||||
@@ -17,36 +28,43 @@ AudioListenerComponent::~AudioListenerComponent() {
|
||||
}
|
||||
|
||||
void AudioListenerComponent::SetMasterVolume(float volume) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_masterVolume = std::max(0.0f, std::min(1.0f, volume));
|
||||
Audio::AudioSystem::Get().SetMasterVolume(m_masterVolume);
|
||||
}
|
||||
|
||||
void AudioListenerComponent::SetMute(bool mute) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_mute = mute;
|
||||
Audio::AudioSystem::Get().SetMuted(m_mute);
|
||||
}
|
||||
|
||||
void AudioListenerComponent::SetDopplerLevel(float level) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_dopplerLevel = std::max(0.0f, std::min(5.0f, level));
|
||||
Audio::AudioSystem::Get().SetListenerDopplerLevel(m_dopplerLevel);
|
||||
}
|
||||
|
||||
void AudioListenerComponent::SetSpeedOfSound(float metersPerSecond) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_speedOfSound = std::max(1.0f, metersPerSecond);
|
||||
Audio::AudioSystem::Get().SetSpeedOfSound(m_speedOfSound);
|
||||
}
|
||||
|
||||
void AudioListenerComponent::SetReverbLevel(float level) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_reverbLevel = std::max(0.0f, std::min(1.0f, level));
|
||||
Audio::AudioSystem::Get().SetListenerReverbLevel(m_reverbLevel);
|
||||
}
|
||||
|
||||
void AudioListenerComponent::SetReverb(Audio::AudioMixer* reverb) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_reverb = reverb;
|
||||
Audio::AudioSystem::Get().SetListenerReverbMixer(m_reverb);
|
||||
}
|
||||
|
||||
void AudioListenerComponent::Update(float deltaTime) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (!m_gameObject) {
|
||||
return;
|
||||
}
|
||||
@@ -67,6 +85,7 @@ void AudioListenerComponent::Update(float deltaTime) {
|
||||
}
|
||||
|
||||
void AudioListenerComponent::Serialize(std::ostream& os) const {
|
||||
auto audioStateLock = LockAudioState();
|
||||
os << "masterVolume=" << m_masterVolume << ";";
|
||||
os << "mute=" << (m_mute ? 1 : 0) << ";";
|
||||
os << "dopplerLevel=" << m_dopplerLevel << ";";
|
||||
@@ -75,6 +94,7 @@ void AudioListenerComponent::Serialize(std::ostream& os) const {
|
||||
}
|
||||
|
||||
void AudioListenerComponent::Deserialize(std::istream& is) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
SetMasterVolume(1.0f);
|
||||
SetMute(false);
|
||||
SetDopplerLevel(1.0f);
|
||||
|
||||
@@ -144,6 +144,13 @@ void ApplyPanToBuffer(float* buffer, Audio::uint32 frameCount, Audio::uint32 cha
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock<std::recursive_mutex> LockAudioState() {
|
||||
if (Audio::AudioSystem* audioSystem = Audio::AudioSystem::TryGetExisting()) {
|
||||
return audioSystem->AcquireStateLock();
|
||||
}
|
||||
return std::unique_lock<std::recursive_mutex>();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AudioSourceComponent::AudioSourceComponent() {
|
||||
@@ -158,6 +165,7 @@ AudioSourceComponent::~AudioSourceComponent() {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::Play() {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (!m_clip || !m_clip->IsValid()) {
|
||||
return;
|
||||
}
|
||||
@@ -189,6 +197,7 @@ void AudioSourceComponent::Play() {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::Pause() {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (m_playState == Audio::PlayState::Playing) {
|
||||
m_playState = Audio::PlayState::Paused;
|
||||
Audio::AudioSystem::Get().UnregisterSource(this);
|
||||
@@ -196,6 +205,7 @@ void AudioSourceComponent::Pause() {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::Stop(Audio::StopMode mode) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (m_playState != Audio::PlayState::Stopped) {
|
||||
m_playState = Audio::PlayState::Stopped;
|
||||
m_samplePosition = 0;
|
||||
@@ -209,6 +219,7 @@ void AudioSourceComponent::Stop(Audio::StopMode mode) {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetClip(Resources::AudioClip* clip) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_clipHandle = Resources::ResourceHandle<Resources::AudioClip>(clip);
|
||||
m_clip = clip;
|
||||
m_clipPath.clear();
|
||||
@@ -235,6 +246,7 @@ void AudioSourceComponent::SetClip(Resources::AudioClip* clip) {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetClipPath(const std::string& clipPath) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_clipRef.Reset();
|
||||
m_clipPath = clipPath;
|
||||
if (!m_clipPath.empty() &&
|
||||
@@ -266,24 +278,29 @@ void AudioSourceComponent::SetClipPath(const std::string& clipPath) {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::ClearClip() {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_clipRef.Reset();
|
||||
m_clipPath.clear();
|
||||
SetClip(nullptr);
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetVolume(float volume) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_volume = std::max(0.0f, std::min(1.0f, volume));
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetPitch(float pitch) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_pitch = std::max(0.0f, std::min(3.0f, pitch));
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetPan(float pan) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_pan = std::max(-1.0f, std::min(1.0f, pan));
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetHRTFEnabled(bool enabled) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_useHRTF = enabled;
|
||||
if (!enabled) {
|
||||
m_hrtf.ResetState();
|
||||
@@ -291,22 +308,27 @@ void AudioSourceComponent::SetHRTFEnabled(bool enabled) {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetHRTFCrossFeed(float crossFeed) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_hrtf.SetCrossFeed(crossFeed);
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetHRTFQuality(Audio::uint32 level) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_hrtf.SetQualityLevel(level);
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetLooping(bool loop) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_isLooping = loop;
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetSpatialize(bool spatialize) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_spatialize = spatialize;
|
||||
}
|
||||
|
||||
void AudioSourceComponent::Set3DParams(const Audio::Audio3DParams& params) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_3DParams = params;
|
||||
m_3DParams.dopplerLevel = std::max(0.0f, m_3DParams.dopplerLevel);
|
||||
m_3DParams.speedOfSound = std::max(1.0f, m_3DParams.speedOfSound);
|
||||
@@ -318,22 +340,27 @@ void AudioSourceComponent::Set3DParams(const Audio::Audio3DParams& params) {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetDopplerLevel(float level) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_3DParams.dopplerLevel = std::max(0.0f, level);
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetSpread(float spread) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_3DParams.spread = std::max(0.0f, std::min(1.0f, spread));
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetReverbZoneMix(float mix) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_3DParams.reverbZoneMix = std::max(0.0f, std::min(1.0f, mix));
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetOutputMixer(Audio::AudioMixer* mixer) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_outputMixer = mixer;
|
||||
}
|
||||
|
||||
void AudioSourceComponent::SetTime(float seconds) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (!m_clip || !m_clip->IsValid()) {
|
||||
return;
|
||||
}
|
||||
@@ -370,15 +397,18 @@ float AudioSourceComponent::GetDuration() const {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::StartEnergyDetect() {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_isEnergyDetecting = true;
|
||||
m_energyHistory.clear();
|
||||
}
|
||||
|
||||
void AudioSourceComponent::StopEnergyDetect() {
|
||||
auto audioStateLock = LockAudioState();
|
||||
m_isEnergyDetecting = false;
|
||||
}
|
||||
|
||||
void AudioSourceComponent::Update(float deltaTime) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (m_gameObject) {
|
||||
const Math::Vector3 position = transform().GetPosition();
|
||||
if (m_hasLastPosition && deltaTime > 0.0f) {
|
||||
@@ -403,12 +433,14 @@ void AudioSourceComponent::Update(float deltaTime) {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::OnEnable() {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (m_playState == Audio::PlayState::Playing) {
|
||||
Audio::AudioSystem::Get().RegisterSource(this);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioSourceComponent::OnDisable() {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (m_playState == Audio::PlayState::Playing) {
|
||||
Audio::AudioSystem::Get().UnregisterSource(this);
|
||||
}
|
||||
@@ -419,6 +451,7 @@ void AudioSourceComponent::OnDestroy() {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::Serialize(std::ostream& os) const {
|
||||
auto audioStateLock = LockAudioState();
|
||||
Resources::AssetRef serializedClipRef = m_clipRef;
|
||||
std::string serializedClipPath = m_clipPath;
|
||||
if (serializedClipPath.empty() && m_clip != nullptr) {
|
||||
@@ -458,6 +491,7 @@ void AudioSourceComponent::Serialize(std::ostream& os) const {
|
||||
}
|
||||
|
||||
void AudioSourceComponent::Deserialize(std::istream& is) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
ClearClip();
|
||||
SetVolume(1.0f);
|
||||
SetPitch(1.0f);
|
||||
@@ -555,6 +589,7 @@ void AudioSourceComponent::ProcessAudio(float* buffer, Audio::uint32 frameCount,
|
||||
float listenerDopplerLevel,
|
||||
float speedOfSound,
|
||||
Audio::uint32 outputSampleRate) {
|
||||
auto audioStateLock = LockAudioState();
|
||||
if (m_playState != Audio::PlayState::Playing || !m_clip) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user