From 5d9dde6589f828093a878f9d4a126988bd2bc263 Mon Sep 17 00:00:00 2001 From: elasota Date: Sun, 25 Apr 2021 00:34:02 -0400 Subject: [PATCH] Refactor audio buffering API, this should mainly prevent SDL audio driver from allocating memory in the mixer callback. --- AerofoilSDL/GpAudioDriver_SDL2.cpp | 222 +++++++++++------- GpApp/Music.cpp | 22 +- GpApp/Sound.cpp | 105 ++++----- .../GpAudioBufferXAudio2.cpp | 67 ++++++ GpAudioDriver_XAudio2/GpAudioBufferXAudio2.h | 29 +++ .../GpAudioChannelXAudio2.cpp | 25 +- GpAudioDriver_XAudio2/GpAudioChannelXAudio2.h | 2 +- .../GpAudioChannelXAudio2Callbacks.cpp | 2 + .../GpAudioDriverXAudio2.cpp | 8 + GpAudioDriver_XAudio2/GpAudioDriverXAudio2.h | 1 + .../GpAudioDriver_XAudio2.vcxproj | 5 + .../GpAudioDriver_XAudio2.vcxproj.filters | 6 + GpCommon/IGpAudioBuffer.h | 8 + GpCommon/IGpAudioChannel.h | 3 +- GpCommon/IGpAudioDriver.h | 2 + PortabilityLayer/PLSound.cpp | 43 ++-- PortabilityLayer/PLSound.h | 4 +- 17 files changed, 374 insertions(+), 180 deletions(-) create mode 100644 GpAudioDriver_XAudio2/GpAudioBufferXAudio2.cpp create mode 100644 GpAudioDriver_XAudio2/GpAudioBufferXAudio2.h create mode 100644 GpCommon/IGpAudioBuffer.h diff --git a/AerofoilSDL/GpAudioDriver_SDL2.cpp b/AerofoilSDL/GpAudioDriver_SDL2.cpp index 3053877..070697d 100644 --- a/AerofoilSDL/GpAudioDriver_SDL2.cpp +++ b/AerofoilSDL/GpAudioDriver_SDL2.cpp @@ -1,4 +1,5 @@ #include "CoreDefs.h" +#include "IGpAudioBuffer.h" #include "IGpAudioDriver.h" #include "IGpAudioChannel.h" #include "IGpAudioChannelCallbacks.h" @@ -13,6 +14,8 @@ #include "SDL_atomic.h" +#include +#include #include #include #include @@ -52,22 +55,82 @@ static void AlignedFree(void *ptr) free(storageLoc); } -struct GpAudioChannelBufferChain_SDL2 final +class GpAudioBuffer_SDL2 final : public IGpAudioBuffer { - GpAudioChannelBufferChain_SDL2(); +public: + static GpAudioBuffer_SDL2 *Create(const void *data, size_t size); - static GpAudioChannelBufferChain_SDL2 *Alloc(); - void Release(); + void AddRef() override; + void Release() override; - static const size_t kMaxCapacity = 65536; + const void *GetData() const; + size_t GetSize() const; - size_t m_consumed; - size_t m_used; - uint8_t m_data[kMaxCapacity]; - GpAudioChannelBufferChain_SDL2 *m_next; - bool m_hasTrigger; +private: + GpAudioBuffer_SDL2(const void *data, size_t size); + ~GpAudioBuffer_SDL2(); + + void Destroy(); + + const void *m_data; + size_t m_size; + SDL_atomic_t m_count; }; +GpAudioBuffer_SDL2 *GpAudioBuffer_SDL2::Create(const void *data, size_t size) +{ + void *storage = malloc(size + sizeof(GpAudioBuffer_SDL2)); + if (!storage) + return nullptr; + + void *dataPos = static_cast(storage) + sizeof(GpAudioBuffer_SDL2); + memcpy(dataPos, data, size); + + return new (storage) GpAudioBuffer_SDL2(dataPos, size); +} + + +void GpAudioBuffer_SDL2::AddRef() +{ + SDL_AtomicAdd(&m_count, 1); +} + +void GpAudioBuffer_SDL2::Release() +{ + int prevCount = SDL_AtomicAdd(&m_count, -1); + if (prevCount == 1) + this->Destroy(); +} + +const void *GpAudioBuffer_SDL2::GetData() const +{ + return m_data; +} + +size_t GpAudioBuffer_SDL2::GetSize() const +{ + return m_size; +} + + +GpAudioBuffer_SDL2::GpAudioBuffer_SDL2(const void *data, size_t size) + : m_data(data) + , m_size(size) +{ + SDL_AtomicSet(&m_count, 1); +} + +GpAudioBuffer_SDL2::~GpAudioBuffer_SDL2() +{ +} + +void GpAudioBuffer_SDL2::Destroy() +{ + this->~GpAudioBuffer_SDL2(); + free(this); +} + + class GpAudioChannel_SDL2 final : public IGpAudioChannel { public: @@ -80,7 +143,7 @@ public: void Release(); void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) override; - void PostBuffer(const void *buffer, size_t bufferSize) override; + bool PostBuffer(IGpAudioBuffer *buffer) override; void Stop() override; void Destroy() override; @@ -91,14 +154,19 @@ public: private: bool Init(GpAudioDriver_SDL2 *driver); + static const size_t kMaxBuffers = 16; + IGpAudioChannelCallbacks *m_callbacks; IGpMutex *m_mutex; GpAudioDriver_SDL2 *m_owner; SDL_atomic_t m_refCount; - GpAudioChannelBufferChain_SDL2 *m_firstPendingBuffer; - GpAudioChannelBufferChain_SDL2 *m_lastPendingBuffer; + GpAudioBuffer_SDL2 *m_pendingBuffers[kMaxBuffers]; + size_t m_nextPendingBufferConsumePos; + size_t m_nextPendingBufferInsertionPos; + size_t m_numQueuedBuffers; + size_t m_firstBufferSamplesConsumed; GpAudioDriver_SDL2_TimePoint_t m_timestamp; // Time that audio will be consumed if posted to the channel, if m_hasTimestamp is true. GpAudioDriver_SDL2_Duration_t m_latency; @@ -118,6 +186,7 @@ public: explicit GpAudioDriver_SDL2(const GpAudioDriverProperties &properties); ~GpAudioDriver_SDL2(); + IGpAudioBuffer *CreateBuffer(const void *data, size_t size) override; IGpAudioChannel *CreateChannel() override; void SetMasterVolume(uint32_t vol, uint32_t maxVolume) override; void Shutdown() override; @@ -162,27 +231,6 @@ private: int16_t m_audioVolumeScale; }; -GpAudioChannelBufferChain_SDL2::GpAudioChannelBufferChain_SDL2() - : m_used(0) - , m_consumed(0) - , m_next(nullptr) - , m_hasTrigger(false) -{ -} - - -GpAudioChannelBufferChain_SDL2 *GpAudioChannelBufferChain_SDL2::Alloc() -{ - void *storage = AlignedAlloc(sizeof(GpAudioChannelBufferChain_SDL2), GP_SYSTEM_MEMORY_ALIGNMENT); - return new (storage) GpAudioChannelBufferChain_SDL2(); -} - -void GpAudioChannelBufferChain_SDL2::Release() -{ - this->~GpAudioChannelBufferChain_SDL2(); - AlignedFree(this); -} - ///////////////////////////////////////////////////////////////////////////////////////// // GpAudioChannel @@ -190,8 +238,6 @@ GpAudioChannel_SDL2::GpAudioChannel_SDL2(GpAudioDriver_SDL2_Duration_t latency, : m_callbacks(nullptr) , m_mutex(nullptr) , m_owner(nullptr) - , m_firstPendingBuffer(nullptr) - , m_lastPendingBuffer(nullptr) , m_latency(latency) , m_bufferTime(bufferTime) , m_bufferSamplesMax(bufferSamplesMax) @@ -199,6 +245,10 @@ GpAudioChannel_SDL2::GpAudioChannel_SDL2(GpAudioDriver_SDL2_Duration_t latency, , m_sampleRate(sampleRate) , m_isMixing(false) , m_hasTimestamp(false) + , m_nextPendingBufferConsumePos(0) + , m_nextPendingBufferInsertionPos(0) + , m_numQueuedBuffers(0) + , m_firstBufferSamplesConsumed(0) { SDL_AtomicSet(&m_refCount, 1); } @@ -210,12 +260,7 @@ GpAudioChannel_SDL2::~GpAudioChannel_SDL2() if (m_mutex) m_mutex->Destroy(); - while (m_firstPendingBuffer) - { - GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer; - m_firstPendingBuffer = buffer->m_next; - buffer->Release(); - } + assert(m_numQueuedBuffers == 0); } void GpAudioChannel_SDL2::AddRef() @@ -238,12 +283,21 @@ void GpAudioChannel_SDL2::SetAudioChannelContext(IGpAudioChannelCallbacks *callb m_callbacks = callbacks; } -void GpAudioChannel_SDL2::PostBuffer(const void *buffer, size_t bufferSize) +bool GpAudioChannel_SDL2::PostBuffer(IGpAudioBuffer *buffer) { + buffer->AddRef(); + m_mutex->Lock(); + if (m_numQueuedBuffers == kMaxBuffers) + { + m_mutex->Unlock(); + buffer->Release(); + return false; + } + size_t leadingSilence = 0; - if (m_firstPendingBuffer == nullptr && m_hasTimestamp && !m_isMixing) + if (m_numQueuedBuffers == 0 && m_hasTimestamp && !m_isMixing) { GpAudioDriver_SDL2_TimePoint_t queueTime = GpAudioDriver_SDL2::GetCurrentTime() + m_latency; if (queueTime > m_timestamp) @@ -262,50 +316,37 @@ void GpAudioChannel_SDL2::PostBuffer(const void *buffer, size_t bufferSize) m_leadingSilence = leadingSilence; - while (bufferSize > 0) - { - GpAudioChannelBufferChain_SDL2 *newBuffer = GpAudioChannelBufferChain_SDL2::Alloc(); - if (newBuffer == nullptr) - break; + m_pendingBuffers[m_nextPendingBufferInsertionPos++] = static_cast(buffer); + m_numQueuedBuffers++; - if (m_lastPendingBuffer == nullptr) - m_firstPendingBuffer = newBuffer; - else - m_lastPendingBuffer->m_next = newBuffer; - m_lastPendingBuffer = newBuffer; - - size_t bufferable = newBuffer->kMaxCapacity; - if (bufferSize < bufferable) - bufferable = bufferSize; - - memcpy(newBuffer->m_data, buffer, bufferable); - - buffer = static_cast(buffer) + bufferable; - bufferSize -= bufferable; - m_lastPendingBuffer->m_used = bufferable; - m_lastPendingBuffer->m_hasTrigger = (bufferSize == 0); - } + m_nextPendingBufferInsertionPos = m_nextPendingBufferInsertionPos % kMaxBuffers; m_mutex->Unlock(); + + return true; } void GpAudioChannel_SDL2::Stop() { m_mutex->Lock(); - GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer; - m_firstPendingBuffer = nullptr; - m_lastPendingBuffer = nullptr; + m_leadingSilence = 0; - while (buffer) + size_t numBuffersToDischarge = m_numQueuedBuffers; + + for (size_t i = 0; i < numBuffersToDischarge; i++) { - if (buffer->m_hasTrigger && m_callbacks) + GpAudioBuffer_SDL2 *buffer = m_pendingBuffers[m_nextPendingBufferConsumePos]; + + m_nextPendingBufferConsumePos = (m_nextPendingBufferConsumePos + 1) % kMaxBuffers; + m_numQueuedBuffers--; + + m_firstBufferSamplesConsumed = 0; + + if (m_callbacks) m_callbacks->NotifyBufferFinished(); - GpAudioChannelBufferChain_SDL2 *nextBuffer = buffer->m_next; buffer->Release(); - - buffer = nextBuffer; } m_mutex->Unlock(); @@ -359,21 +400,27 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2 } } - while (m_firstPendingBuffer != nullptr) + while (m_numQueuedBuffers > 0) { - GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer; - const size_t available = (buffer->m_used - buffer->m_consumed); + GpAudioBuffer_SDL2 *buffer = m_pendingBuffers[m_nextPendingBufferConsumePos]; + const void *bufferData = buffer->GetData(); + const size_t bufferSize = buffer->GetSize(); + + assert(m_firstBufferSamplesConsumed < bufferSize); + + const size_t available = (bufferSize - m_firstBufferSamplesConsumed); if (available <= sz) { - memcpy(output, buffer->m_data + buffer->m_consumed, available); + memcpy(output, static_cast(bufferData) + m_firstBufferSamplesConsumed, available); sz -= available; output += available; - m_firstPendingBuffer = buffer->m_next; - if (m_firstPendingBuffer == nullptr) - m_lastPendingBuffer = nullptr; + m_nextPendingBufferConsumePos = (m_nextPendingBufferConsumePos + 1) % kMaxBuffers; + m_numQueuedBuffers--; - if (buffer->m_hasTrigger && m_callbacks) + m_firstBufferSamplesConsumed = 0; + + if (m_callbacks) m_callbacks->NotifyBufferFinished(); buffer->Release(); @@ -383,9 +430,9 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2 } else { - memcpy(output, buffer->m_data + buffer->m_consumed, sz); - buffer->m_consumed += sz; - buffer += sz; + memcpy(output, static_cast(bufferData) + m_firstBufferSamplesConsumed, sz); + m_firstBufferSamplesConsumed += sz; + output += sz; sz = 0; break; } @@ -446,6 +493,11 @@ GpAudioDriver_SDL2::~GpAudioDriver_SDL2() m_mutex->Destroy(); } +IGpAudioBuffer *GpAudioDriver_SDL2::CreateBuffer(const void *data, size_t size) +{ + return GpAudioBuffer_SDL2::Create(data, size); +} + IGpAudioChannel *GpAudioDriver_SDL2::CreateChannel() { GpAudioChannel_SDL2 *newChannel = GpAudioChannel_SDL2::Alloc(this, m_latency, m_bufferTime, m_bufferSamples, m_sampleRate); diff --git a/GpApp/Music.cpp b/GpApp/Music.cpp index bb29136..a6ae546 100644 --- a/GpApp/Music.cpp +++ b/GpApp/Music.cpp @@ -8,6 +8,7 @@ #include "Environ.h" #include "Externs.h" #include "SoundSync.h" +#include "IGpAudioBuffer.h" #include "IGpMutex.h" #include "IGpSystemServices.h" #include "MemoryManager.h" @@ -29,11 +30,11 @@ PLError_t DumpMusicSounds (void); PLError_t OpenMusicChannel (void); PLError_t CloseMusicChannel (void); -THandle ParseAndConvertSound(const THandle &handle); +IGpAudioBuffer *ParseAndConvertSound(const THandle &handle); PortabilityLayer::AudioChannel *musicChannel; -Ptr theMusicData[kMaxMusic]; +IGpAudioBuffer *theMusicData[kMaxMusic]; short musicScore[kLastMusicPiece]; short gameScore[kLastGamePiece]; Boolean isMusicOn, isPlayMusicIdle, isPlayMusicGame; @@ -208,22 +209,23 @@ PLError_t LoadMusicSounds (void) theErr = PLErrors::kNone; for (i = 0; i < kMaxMusic; i++) - theMusicData[i] = nil; + { + assert(theMusicData[i] == nil); + } for (i = 0; i < kMaxMusic; i++) { - theSound = ParseAndConvertSound(PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('snd ', i + kBaseBufferMusicID)); + theSound = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('snd ', i + kBaseBufferMusicID); if (theSound == nil) return PLErrors::kOutOfMemory; - soundDataSize = GetHandleSize(theSound); + IGpAudioBuffer *buffer = ParseAndConvertSound(theSound); + theSound.Dispose(); - theMusicData[i] = PortabilityLayer::MemoryManager::GetInstance()->Alloc(soundDataSize); - if (theMusicData[i] == nil) + if (buffer == nil) return PLErrors::kOutOfMemory; - memcpy(theMusicData[i], static_cast(*theSound), soundDataSize); - theSound.Dispose(); + theMusicData[i] = buffer; } return (theErr); } @@ -240,7 +242,7 @@ PLError_t DumpMusicSounds (void) for (i = 0; i < kMaxMusic; i++) { if (theMusicData[i] != nil) - PortabilityLayer::MemoryManager::GetInstance()->Release(theMusicData[i]); + theMusicData[i]->Release(); theMusicData[i] = nil; } diff --git a/GpApp/Sound.cpp b/GpApp/Sound.cpp index c69b0dc..7ce11e8 100644 --- a/GpApp/Sound.cpp +++ b/GpApp/Sound.cpp @@ -10,6 +10,8 @@ #include "PLSound.h" #include "DialogManager.h" #include "Externs.h" +#include "IGpAudioBuffer.h" +#include "IGpAudioDriver.h" #include "IGpLogDriver.h" #include "MemoryManager.h" #include "ResourceManager.h" @@ -31,10 +33,10 @@ PLError_t LoadBufferSounds (void); void DumpBufferSounds (void); PLError_t OpenSoundChannels (void); void CloseSoundChannels (void); -THandle ParseAndConvertSound(const THandle &handle); +IGpAudioBuffer *ParseAndConvertSound(const THandle &handle); PortabilityLayer::AudioChannel *channel0, *channel1, *channel2; -Ptr theSoundData[kMaxSounds]; +IGpAudioBuffer *theSoundData[kMaxSounds]; short numSoundsLoaded; Boolean soundLoaded[kMaxSounds], dontLoadSounds; Boolean channelOpen, isSoundOn, failedSound; @@ -192,24 +194,20 @@ PLError_t LoadTriggerSound (short soundID) theErr = PLErrors::kNone; - theSound = ParseAndConvertSound(LoadHouseResource('snd ', soundID)); + theSound = LoadHouseResource('snd ', soundID); if (theSound == nil) - { - theErr = PLErrors::kFileNotFound; - } + theErr = PLErrors::kResourceError; else { - soundDataSize = GetHandleSize(theSound); - theSoundData[kMaxSounds - 1] = PortabilityLayer::MemoryManager::GetInstance()->Alloc(soundDataSize); - if (theSoundData[kMaxSounds - 1] == nil) - { - theSound.Dispose(); - theErr = PLErrors::kOutOfMemory; - } + IGpAudioBuffer *buffer = ParseAndConvertSound(theSound); + theSound.Dispose(); + + if (buffer == nil) + theErr = PLErrors::kResourceError; else { - memcpy(theSoundData[kMaxSounds - 1], (Byte*)(*theSound), soundDataSize); - theSound.Dispose(); + assert(theSoundData[kMaxSounds - 1] == nil); + theSoundData[kMaxSounds - 1] = buffer; } } } @@ -222,7 +220,7 @@ PLError_t LoadTriggerSound (short soundID) void DumpTriggerSound (void) { if (theSoundData[kMaxSounds - 1] != nil) - PortabilityLayer::MemoryManager::GetInstance()->Release(theSoundData[kMaxSounds - 1]); + theSoundData[kMaxSounds - 1]->Release(); theSoundData[kMaxSounds - 1] = nil; } @@ -239,21 +237,21 @@ PLError_t LoadBufferSounds (void) for (i = 0; i < kMaxSounds - 1; i++) { - theSound = ParseAndConvertSound(PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('snd ', i + kBaseBufferSoundID)); + theSound = PortabilityLayer::ResourceManager::GetInstance()->GetAppResource('snd ', i + kBaseBufferSoundID); if (theSound == nil) - return (PLErrors::kOutOfMemory); - - soundDataSize = GetHandleSize(theSound); - - theSoundData[i] = PortabilityLayer::MemoryManager::GetInstance()->Alloc(soundDataSize); - if (theSoundData[i] == nil) - return (PLErrors::kOutOfMemory); - - memcpy(theSoundData[i], *theSound, soundDataSize); + return (PLErrors::kResourceError); + + IGpAudioBuffer *buffer = ParseAndConvertSound(theSound); theSound.Dispose(); + + if (!buffer) + return PLErrors::kResourceError; + + assert(theSoundData[i] == nil); + theSoundData[i] = buffer; } - - theSoundData[kMaxSounds - 1] = nil; + + assert(theSoundData[kMaxSounds - 1] == nil); return (theErr); } @@ -267,7 +265,7 @@ void DumpBufferSounds (void) for (i = 0; i < kMaxSounds; i++) { if (theSoundData[i] != nil) - PortabilityLayer::MemoryManager::GetInstance()->Release(theSoundData[i]); + theSoundData[i]->Release(); theSoundData[i] = nil; } } @@ -406,23 +404,23 @@ void TellHerNoSounds (void) //-------------------------------------------------------------- ParseAndConvertSound -THandle ParseAndConvertSoundChecked(const THandle &handle) +IGpAudioBuffer *ParseAndConvertSoundChecked(const THandle &handle) { const uint8_t *dataStart = static_cast(*handle); const size_t size = handle.MMBlock()->m_size; if (size < sizeof(PortabilityLayer::RIFFTag)) - return THandle(); + return nullptr; PortabilityLayer::RIFFTag mainRiffTag; memcpy(&mainRiffTag, dataStart, sizeof(PortabilityLayer::RIFFTag)); if (mainRiffTag.m_tag != PortabilityLayer::WaveConstants::kRiffChunkID) - return THandle(); + return nullptr; const uint32_t riffSize = mainRiffTag.m_chunkSize; if (riffSize < 4 || riffSize - 4 > size - sizeof(PortabilityLayer::RIFFTag)) - return THandle(); + return nullptr; const uint8_t *riffStart = dataStart + sizeof(PortabilityLayer::RIFFTag); const uint8_t *riffEnd = riffStart + riffSize; @@ -434,7 +432,7 @@ THandle ParseAndConvertSoundChecked(const THandle &handle) memcpy(&waveMarker, riffStart, 4); if (waveMarker != PortabilityLayer::WaveConstants::kWaveChunkID) - return THandle(); + return nullptr; const uint8_t *tagSearchLoc = riffStart + 4; @@ -442,7 +440,7 @@ THandle ParseAndConvertSoundChecked(const THandle &handle) while (tagSearchLoc != riffEnd) { if (riffEnd - tagSearchLoc < sizeof(PortabilityLayer::RIFFTag)) - return THandle(); + return nullptr; PortabilityLayer::RIFFTag riffTag; memcpy(&riffTag, tagSearchLoc, sizeof(PortabilityLayer::RIFFTag)); @@ -455,20 +453,20 @@ THandle ParseAndConvertSoundChecked(const THandle &handle) const uint32_t riffTagSizeUnpadded = riffTag.m_chunkSize; if (riffTagSizeUnpadded == 0xffffffffU) - return THandle(); + return nullptr; const uint32_t riffTagSizePadded = riffTagSizeUnpadded + (riffTagSizeUnpadded & 1); tagSearchLoc += sizeof(PortabilityLayer::RIFFTag); if (riffEnd - tagSearchLoc < riffTagSizePadded) - return THandle(); + return nullptr; tagSearchLoc += riffTagSizePadded; } if (formatTagLoc == nullptr || dataTagLoc == nullptr) - return THandle(); + return nullptr; PortabilityLayer::RIFFTag fmtTag; memcpy(&fmtTag, formatTagLoc, sizeof(PortabilityLayer::RIFFTag)); @@ -502,7 +500,7 @@ THandle ParseAndConvertSoundChecked(const THandle &handle) copyableSize = sizeof(PortabilityLayer::WaveFormatChunkV1); } else - return THandle(); + return nullptr; memcpy(&formatChunkV3, formatContents, copyableSize); @@ -510,35 +508,30 @@ THandle ParseAndConvertSoundChecked(const THandle &handle) const PortabilityLayer::WaveFormatChunkV1 formatChunkV1 = formatChunkV2.m_v1; if (formatChunkV1.m_bitsPerSample != 8) - return THandle(); + return nullptr; if (formatChunkV1.m_formatCode != PortabilityLayer::WaveConstants::kFormatPCM || formatChunkV1.m_numChannels != 1 || formatChunkV1.m_blockAlignmentBytes != 1 || formatChunkV1.m_bitsPerSample != 8) - return THandle(); - - THandle convertedHandle = THandle(PortabilityLayer::MemoryManager::GetInstance()->AllocHandle(4 + dataTag.m_chunkSize)); - if (!convertedHandle) - return THandle(); - - uint8_t *handleData = static_cast(*convertedHandle); + return nullptr; uint32_t dataSize = dataTag.m_chunkSize; - memcpy(handleData, &dataSize, 4); + if (dataSize > 0x1000000) + return nullptr; - memcpy(handleData + 4, dataContents, dataSize); + IGpAudioDriver *audioDriver = PLDrivers::GetAudioDriver(); + if (!audioDriver) + return nullptr; - return convertedHandle; + return audioDriver->CreateBuffer(dataContents, dataSize); } -THandle ParseAndConvertSound(const THandle &handle) +IGpAudioBuffer *ParseAndConvertSound(const THandle &handle) { if (!handle) - return THandle(); + return nullptr; - THandle converted = ParseAndConvertSoundChecked(handle); - handle.Dispose(); - - return converted; + IGpAudioBuffer *buffer = ParseAndConvertSoundChecked(handle); + return buffer; } diff --git a/GpAudioDriver_XAudio2/GpAudioBufferXAudio2.cpp b/GpAudioDriver_XAudio2/GpAudioBufferXAudio2.cpp new file mode 100644 index 0000000..780fb37 --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioBufferXAudio2.cpp @@ -0,0 +1,67 @@ +#include "GpAudioBufferXAudio2.h" +#include "CoreDefs.h" +#include "GpWindows.h" + +#include +#include +#include + +GpAudioBufferXAudio2 *GpAudioBufferXAudio2::Create(const void *buffer, size_t size) +{ + size_t baseSize = sizeof(GpAudioBufferXAudio2); + baseSize = baseSize + GP_SYSTEM_MEMORY_ALIGNMENT - 1; + baseSize -= baseSize % GP_SYSTEM_MEMORY_ALIGNMENT; + + size_t totalSize = baseSize + size; + + void *storage = _aligned_malloc(totalSize, GP_SYSTEM_MEMORY_ALIGNMENT); + if (!storage) + return nullptr; + + void *dataPos = static_cast(storage) + baseSize; + + memcpy(dataPos, buffer, size); + return new (storage) GpAudioBufferXAudio2(dataPos, size); +} + +void GpAudioBufferXAudio2::AddRef() +{ + InterlockedIncrement(&m_count); +} + +void GpAudioBufferXAudio2::Release() +{ + if (InterlockedDecrement(&m_count) == 0) + this->Destroy(); +} + +const XAUDIO2_BUFFER *GpAudioBufferXAudio2::GetXA2Buffer() const +{ + return &m_xa2Buffer; +} + +GpAudioBufferXAudio2::GpAudioBufferXAudio2(const void *data, size_t size) + : m_data(data) + , m_size(size) + , m_count(1) +{ + m_xa2Buffer.Flags = 0; + m_xa2Buffer.AudioBytes = static_cast(size); + m_xa2Buffer.pAudioData = static_cast(data); + m_xa2Buffer.PlayBegin = 0; + m_xa2Buffer.PlayLength = 0; + m_xa2Buffer.LoopBegin = 0; + m_xa2Buffer.LoopLength = 0; + m_xa2Buffer.LoopCount = 0; + m_xa2Buffer.pContext = this; +} + +GpAudioBufferXAudio2::~GpAudioBufferXAudio2() +{ +} + +void GpAudioBufferXAudio2::Destroy() +{ + this->~GpAudioBufferXAudio2(); + _aligned_free(this); +} diff --git a/GpAudioDriver_XAudio2/GpAudioBufferXAudio2.h b/GpAudioDriver_XAudio2/GpAudioBufferXAudio2.h new file mode 100644 index 0000000..c17e10e --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioBufferXAudio2.h @@ -0,0 +1,29 @@ +#pragma once + +#include "IGpAudioBuffer.h" + +#include + +class GpAudioBufferXAudio2 final : public IGpAudioBuffer +{ +public: + static GpAudioBufferXAudio2 *Create(const void *buffer, size_t size); + + void AddRef() override; + void Release() override; + + const XAUDIO2_BUFFER *GetXA2Buffer() const; + +private: + GpAudioBufferXAudio2(const void *data, size_t size); + ~GpAudioBufferXAudio2(); + + void Destroy(); + + const void *m_data; + size_t m_size; + + XAUDIO2_BUFFER m_xa2Buffer; + + volatile unsigned int m_count; +}; diff --git a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.cpp b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.cpp index 54a13d0..fc7e858 100644 --- a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.cpp +++ b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.cpp @@ -1,3 +1,4 @@ +#include "GpAudioBufferXAudio2.h" #include "GpAudioChannelXAudio2.h" #include "GpAudioDriverXAudio2.h" #include "IGpAudioChannelCallbacks.h" @@ -77,25 +78,25 @@ void GpAudioChannelXAudio2::SetAudioChannelContext(IGpAudioChannelCallbacks *cal m_contextCallbacks = callbacks; } -void GpAudioChannelXAudio2::PostBuffer(const void *buffer, size_t bufferSize) +bool GpAudioChannelXAudio2::PostBuffer(IGpAudioBuffer *buffer) { - XAUDIO2_BUFFER xa2Buffer; - xa2Buffer.Flags = 0; - xa2Buffer.AudioBytes = static_cast(bufferSize); - xa2Buffer.pAudioData = static_cast(buffer); - xa2Buffer.PlayBegin = 0; - xa2Buffer.PlayLength = 0; - xa2Buffer.LoopBegin = 0; - xa2Buffer.LoopLength = 0; - xa2Buffer.LoopCount = 0; - xa2Buffer.pContext = nullptr; + GpAudioBufferXAudio2 *xa2Buffer = static_cast(buffer); + xa2Buffer->AddRef(); + + HRESULT result = m_sourceVoice->SubmitSourceBuffer(xa2Buffer->GetXA2Buffer(), nullptr); + if (result != S_OK) + { + xa2Buffer->Release(); + return false; + } - m_sourceVoice->SubmitSourceBuffer(&xa2Buffer, nullptr); if (m_voiceState == VoiceState_Idle) { m_voiceState = VoiceState_Active; m_sourceVoice->Start(0, 0); } + + return true; } void GpAudioChannelXAudio2::Stop() diff --git a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.h b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.h index b9317b2..31dd4dd 100644 --- a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.h +++ b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.h @@ -16,7 +16,7 @@ public: static GpAudioChannelXAudio2 *Create(GpAudioDriverXAudio2 *driver); void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) override; - void PostBuffer(const void *buffer, size_t bufferSize) override; + bool PostBuffer(IGpAudioBuffer *buffer) override; void Stop() override; void Destroy() override; diff --git a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.cpp b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.cpp index 0291043..8016f28 100644 --- a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.cpp +++ b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.cpp @@ -1,3 +1,4 @@ +#include "GpAudioBufferXAudio2.h" #include "GpAudioChannelXAudio2Callbacks.h" #include "GpAudioChannelXAudio2.h" @@ -24,6 +25,7 @@ void GpAudioChannelXAudio2Callbacks::OnBufferStart(void* pBufferContext) void GpAudioChannelXAudio2Callbacks::OnBufferEnd(void* pBufferContext) { + static_cast(pBufferContext)->Release(); m_owner->OnBufferEnd(); } diff --git a/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.cpp b/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.cpp index dfdb0ef..5b30b9a 100644 --- a/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.cpp +++ b/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.cpp @@ -1,9 +1,12 @@ #include "GpAudioDriverXAudio2.h" #include "IGpLogDriver.h" +#include "GpAudioBufferXAudio2.h" #include "GpAudioChannelXAudio2.h" +#include "CoreDefs.h" #include +#include void GpAudioDriverXAudio2::Shutdown() { @@ -96,6 +99,11 @@ GpAudioDriverXAudio2 *GpAudioDriverXAudio2::Create(const GpAudioDriverProperties return new GpAudioDriverXAudio2(properties, realSampleRate, xa, mv); } +IGpAudioBuffer *GpAudioDriverXAudio2::CreateBuffer(const void *buffer, size_t bufferSize) +{ + return GpAudioBufferXAudio2::Create(buffer, bufferSize); +} + IGpAudioChannel *GpAudioDriverXAudio2::CreateChannel() { return GpAudioChannelXAudio2::Create(this); diff --git a/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.h b/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.h index 4a9c070..563034f 100644 --- a/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.h +++ b/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.h @@ -10,6 +10,7 @@ struct IXAudio2MasteringVoice; class GpAudioDriverXAudio2 : public IGpAudioDriver { public: + IGpAudioBuffer *CreateBuffer(const void *buffer, size_t bufferSize) override; IGpAudioChannel *CreateChannel() override; void SetMasterVolume(uint32_t vol, uint32_t maxVolume) override; void Shutdown() override; diff --git a/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj b/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj index 4af8dee..44a373a 100644 --- a/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj +++ b/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj @@ -38,11 +38,14 @@ + + + @@ -69,12 +72,14 @@ + + diff --git a/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj.filters b/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj.filters index 26eedc7..cc33b93 100644 --- a/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj.filters +++ b/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj.filters @@ -27,6 +27,9 @@ Header Files + + Header Files + @@ -41,6 +44,9 @@ Source Files + + Source Files + diff --git a/GpCommon/IGpAudioBuffer.h b/GpCommon/IGpAudioBuffer.h new file mode 100644 index 0000000..89be8de --- /dev/null +++ b/GpCommon/IGpAudioBuffer.h @@ -0,0 +1,8 @@ +#pragma once + +struct IGpAudioBuffer +{ +public: + virtual void AddRef() = 0; + virtual void Release() = 0; +}; diff --git a/GpCommon/IGpAudioChannel.h b/GpCommon/IGpAudioChannel.h index 15a8699..3e18dd3 100644 --- a/GpCommon/IGpAudioChannel.h +++ b/GpCommon/IGpAudioChannel.h @@ -3,11 +3,12 @@ #include struct IGpAudioChannelCallbacks; +struct IGpAudioBuffer; struct IGpAudioChannel { virtual void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) = 0; - virtual void PostBuffer(const void *buffer, size_t bufferSize) = 0; + virtual bool PostBuffer(IGpAudioBuffer *buffer) = 0; virtual void Stop() = 0; virtual void Destroy() = 0; }; diff --git a/GpCommon/IGpAudioDriver.h b/GpCommon/IGpAudioDriver.h index 9f77acb..31c086a 100644 --- a/GpCommon/IGpAudioDriver.h +++ b/GpCommon/IGpAudioDriver.h @@ -4,10 +4,12 @@ struct IGpAudioChannel; struct IGpPrefsHandler; +struct IGpAudioBuffer; struct IGpAudioDriver { public: + virtual IGpAudioBuffer *CreateBuffer(const void *buffer, size_t bufferSize) = 0; virtual IGpAudioChannel *CreateChannel() = 0; virtual void SetMasterVolume(uint32_t vol, uint32_t maxVolume) = 0; diff --git a/PortabilityLayer/PLSound.cpp b/PortabilityLayer/PLSound.cpp index ade717d..9b9b0fa 100644 --- a/PortabilityLayer/PLSound.cpp +++ b/PortabilityLayer/PLSound.cpp @@ -1,6 +1,7 @@ #include "PLSound.h" #include "MemoryManager.h" +#include "IGpAudioBuffer.h" #include "IGpMutex.h" #include "IGpThreadEvent.h" #include "IGpAudioChannel.h" @@ -30,7 +31,7 @@ namespace PortabilityLayer { union AudioCommandParam { - const void *m_ptr; + IGpAudioBuffer *m_buffer; AudioChannelCallback_t m_callback; }; @@ -45,7 +46,7 @@ namespace PortabilityLayer ~AudioChannelImpl(); void Destroy(bool wait) override; - bool AddBuffer(const void *lengthTaggedBuffer, bool blocking) override; + bool AddBuffer(IGpAudioBuffer *buffer, bool blocking) override; bool AddCallback(AudioChannelCallback_t callback, bool blocking) override; void ClearAllCommands() override; void Stop() override; @@ -70,7 +71,7 @@ namespace PortabilityLayer static const unsigned int kMaxQueuedCommands = 64; void DigestQueueItems(); - void DigestBufferCommand(const void *dataPointer); + void DigestBufferCommand(IGpAudioBuffer *buffer); IGpAudioChannel *m_audioChannel; @@ -103,6 +104,16 @@ namespace PortabilityLayer m_mutex->Destroy(); m_threadEvent->Destroy(); m_audioChannel->Destroy(); + + while (m_numQueuedCommands) + { + const AudioCommand &command = m_commandQueue[m_nextDequeueCommandPos]; + m_numQueuedCommands--; + m_nextDequeueCommandPos = (m_nextDequeueCommandPos + 1) % static_cast(kMaxQueuedCommands); + + if (command.m_commandType == AudioCommandTypes::kBuffer) + command.m_param.m_buffer->Release(); + } } void AudioChannelImpl::NotifyBufferFinished() @@ -167,20 +178,26 @@ namespace PortabilityLayer PortabilityLayer::MemoryManager::GetInstance()->Release(this); } - bool AudioChannelImpl::AddBuffer(const void *lengthTaggedBuffer, bool blocking) + bool AudioChannelImpl::AddBuffer(IGpAudioBuffer *buffer, bool blocking) { + buffer->AddRef(); + AudioCommand cmd; cmd.m_commandType = AudioCommandTypes::kBuffer; - cmd.m_param.m_ptr = lengthTaggedBuffer; + cmd.m_param.m_buffer = buffer; - return this->PushCommand(cmd, blocking); + bool pushedOK = this->PushCommand(cmd, blocking); + if (!pushedOK) + buffer->Release(); + + return pushedOK; } bool AudioChannelImpl::AddCallback(AudioChannelCallback_t callback, bool blocking) { AudioCommand cmd; cmd.m_commandType = AudioCommandTypes::kCallback; - cmd.m_param.m_ptr = reinterpret_cast(callback); + cmd.m_param.m_callback = callback; return this->PushCommand(cmd, blocking); } @@ -200,7 +217,7 @@ namespace PortabilityLayer switch (command.m_commandType) { case AudioCommandTypes::kBuffer: - DigestBufferCommand(command.m_param.m_ptr); + DigestBufferCommand(command.m_param.m_buffer); assert(m_state == State_PlayingAsync); m_mutex->Unlock(); return; @@ -222,16 +239,14 @@ namespace PortabilityLayer m_mutex->Unlock(); } - void AudioChannelImpl::DigestBufferCommand(const void *dataPointer) + void AudioChannelImpl::DigestBufferCommand(IGpAudioBuffer *buffer) { assert(m_state == State_Idle); - // At this point, the buffer should already be validated and converted, and the data pointer should point at the data tag - uint32_t length; - memcpy(&length, dataPointer, 4); + if (m_audioChannel->PostBuffer(buffer)) + m_state = State_PlayingAsync; - m_audioChannel->PostBuffer(static_cast(dataPointer) + 4, length); - m_state = State_PlayingAsync; + buffer->Release(); } bool AudioChannelImpl::PushCommand(const AudioCommand &command, bool blocking) diff --git a/PortabilityLayer/PLSound.h b/PortabilityLayer/PLSound.h index 4c2d528..bc94151 100644 --- a/PortabilityLayer/PLSound.h +++ b/PortabilityLayer/PLSound.h @@ -2,6 +2,8 @@ #include "PLCore.h" +struct IGpAudioBuffer; + namespace PortabilityLayer { struct AudioChannel; @@ -11,7 +13,7 @@ namespace PortabilityLayer struct AudioChannel { virtual void Destroy(bool wait) = 0; - virtual bool AddBuffer(const void *lengthTaggedBuffer, bool blocking) = 0; + virtual bool AddBuffer(IGpAudioBuffer *buffer, bool blocking) = 0; virtual bool AddCallback(AudioChannelCallback_t callback, bool blocking) = 0; virtual void ClearAllCommands() = 0; virtual void Stop() = 0;