Preprocess audio into S16 for faster mixing.

This commit is contained in:
elasota
2021-04-29 20:16:24 -04:00
parent 7442b92dd3
commit f24b0a0e77
2 changed files with 55 additions and 39 deletions

View File

@@ -58,33 +58,41 @@ static void AlignedFree(void *ptr)
class GpAudioBuffer_SDL2 final : public IGpAudioBuffer class GpAudioBuffer_SDL2 final : public IGpAudioBuffer
{ {
public: public:
typedef int16_t AudioSample_t;
static GpAudioBuffer_SDL2 *Create(const void *data, size_t size); static GpAudioBuffer_SDL2 *Create(const void *data, size_t size);
void AddRef() override; void AddRef() override;
void Release() override; void Release() override;
const void *GetData() const; const AudioSample_t *GetData() const;
size_t GetSize() const; size_t GetSize() const;
private: private:
GpAudioBuffer_SDL2(const void *data, size_t size); GpAudioBuffer_SDL2(const AudioSample_t *data, size_t size);
~GpAudioBuffer_SDL2(); ~GpAudioBuffer_SDL2();
void Destroy(); void Destroy();
const void *m_data; const AudioSample_t *m_data;
size_t m_size; size_t m_size;
SDL_atomic_t m_count; SDL_atomic_t m_count;
}; };
GpAudioBuffer_SDL2 *GpAudioBuffer_SDL2::Create(const void *data, size_t size) GpAudioBuffer_SDL2 *GpAudioBuffer_SDL2::Create(const void *data, size_t size)
{ {
void *storage = malloc(size + sizeof(GpAudioBuffer_SDL2)); size_t baseSize = sizeof(GpAudioBuffer_SDL2) + GP_SYSTEM_MEMORY_ALIGNMENT + 1;
baseSize -= baseSize % GP_SYSTEM_MEMORY_ALIGNMENT;
void *storage = malloc(size * sizeof(AudioSample_t) + baseSize);
if (!storage) if (!storage)
return nullptr; return nullptr;
void *dataPos = static_cast<uint8_t*>(storage) + sizeof(GpAudioBuffer_SDL2); AudioSample_t *dataPos = reinterpret_cast<AudioSample_t*>(static_cast<uint8_t*>(storage) + baseSize);
memcpy(dataPos, data, size);
// Convert from u8 to s16
const uint8_t *srcData = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < size; i++)
dataPos[i] = srcData[i] - 0x80;
return new (storage) GpAudioBuffer_SDL2(dataPos, size); return new (storage) GpAudioBuffer_SDL2(dataPos, size);
} }
@@ -102,7 +110,7 @@ void GpAudioBuffer_SDL2::Release()
this->Destroy(); this->Destroy();
} }
const void *GpAudioBuffer_SDL2::GetData() const const int16_t *GpAudioBuffer_SDL2::GetData() const
{ {
return m_data; return m_data;
} }
@@ -113,7 +121,7 @@ size_t GpAudioBuffer_SDL2::GetSize() const
} }
GpAudioBuffer_SDL2::GpAudioBuffer_SDL2(const void *data, size_t size) GpAudioBuffer_SDL2::GpAudioBuffer_SDL2(const int16_t *data, size_t size)
: m_data(data) : m_data(data)
, m_size(size) , m_size(size)
{ {
@@ -135,6 +143,7 @@ class GpAudioChannel_SDL2 final : public IGpAudioChannel
{ {
public: public:
friend class GpAudioDriver_SDL2; friend class GpAudioDriver_SDL2;
typedef GpAudioBuffer_SDL2::AudioSample_t AudioSample_t;
GpAudioChannel_SDL2(GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate); GpAudioChannel_SDL2(GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate);
~GpAudioChannel_SDL2(); ~GpAudioChannel_SDL2();
@@ -147,7 +156,7 @@ public:
void Stop() override; void Stop() override;
void Destroy() override; void Destroy() override;
void Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime); void Consume(int16_t *output, size_t sz, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime);
static GpAudioChannel_SDL2 *Alloc(GpAudioDriver_SDL2 *driver, GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate); static GpAudioChannel_SDL2 *Alloc(GpAudioDriver_SDL2 *driver, GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate);
@@ -183,6 +192,9 @@ class GpAudioDriver_SDL2 final : public IGpAudioDriver, public IGpPrefsHandler
public: public:
friend class GpAudioChannel_SDL2; friend class GpAudioChannel_SDL2;
typedef GpAudioChannel_SDL2::AudioSample_t AudioSample_t;
explicit GpAudioDriver_SDL2(const GpAudioDriverProperties &properties); explicit GpAudioDriver_SDL2(const GpAudioDriverProperties &properties);
~GpAudioDriver_SDL2(); ~GpAudioDriver_SDL2();
@@ -206,13 +218,14 @@ private:
void MixAudio(void *stream, size_t len); void MixAudio(void *stream, size_t len);
void RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels, size_t maxSamplesToFill, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime); void RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels, size_t maxSamplesToFill, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime);
static void AddSamples(AudioSample_t *GP_RESTRICT dest, const AudioSample_t *GP_RESTRICT src, size_t nSamples);
GpAudioDriverProperties m_properties; GpAudioDriverProperties m_properties;
IGpMutex *m_mutex; IGpMutex *m_mutex;
IGpMutex *m_mixState; IGpMutex *m_mixState;
static const size_t kMaxChannels = 16; static const size_t kMaxChannels = 16;
static const size_t kMixChunkSize = 512; static const size_t kMixChunkSamples = 512;
static const int16_t kMaxAudioVolumeScale = 64; static const int16_t kMaxAudioVolumeScale = 64;
GpAudioChannel_SDL2 *m_channels[kMaxChannels]; GpAudioChannel_SDL2 *m_channels[kMaxChannels];
@@ -225,7 +238,7 @@ private:
bool m_sdlAudioRunning; bool m_sdlAudioRunning;
GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) int16_t m_mixChunk[kMixChunkSize]; GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) int16_t m_mixChunk[kMixChunkSamples];
size_t m_mixChunkReadOffset; size_t m_mixChunkReadOffset;
int16_t m_audioVolumeScale; int16_t m_audioVolumeScale;
@@ -370,7 +383,7 @@ bool GpAudioChannel_SDL2::Init(GpAudioDriver_SDL2 *driver)
return true; return true;
} }
void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime) void GpAudioChannel_SDL2::Consume(int16_t *output, size_t sz, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime)
{ {
m_mutex->Lock(); m_mutex->Lock();
@@ -380,7 +393,7 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2
if (sz <= m_leadingSilence) if (sz <= m_leadingSilence)
{ {
memset(output, 0x80, sz); memset(output, 0, sz * sizeof(AudioSample_t));
m_leadingSilence -= sz; m_leadingSilence -= sz;
m_isMixing = false; m_isMixing = false;
@@ -392,7 +405,7 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2
size_t leadingSilence = m_leadingSilence; size_t leadingSilence = m_leadingSilence;
if (leadingSilence > 0) if (leadingSilence > 0)
{ {
memset(output, 0x80, leadingSilence); memset(output, 0, leadingSilence * sizeof(AudioSample_t));
output += leadingSilence; output += leadingSilence;
sz -= leadingSilence; sz -= leadingSilence;
@@ -403,7 +416,7 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2
while (m_numQueuedBuffers > 0) while (m_numQueuedBuffers > 0)
{ {
GpAudioBuffer_SDL2 *buffer = m_pendingBuffers[m_nextPendingBufferConsumePos]; GpAudioBuffer_SDL2 *buffer = m_pendingBuffers[m_nextPendingBufferConsumePos];
const void *bufferData = buffer->GetData(); const int16_t *bufferData = buffer->GetData();
const size_t bufferSize = buffer->GetSize(); const size_t bufferSize = buffer->GetSize();
assert(m_firstBufferSamplesConsumed < bufferSize); assert(m_firstBufferSamplesConsumed < bufferSize);
@@ -411,7 +424,7 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2
const size_t available = (bufferSize - m_firstBufferSamplesConsumed); const size_t available = (bufferSize - m_firstBufferSamplesConsumed);
if (available <= sz) if (available <= sz)
{ {
memcpy(output, static_cast<const uint8_t*>(bufferData) + m_firstBufferSamplesConsumed, available); memcpy(output, bufferData + m_firstBufferSamplesConsumed, available * sizeof(AudioSample_t));
sz -= available; sz -= available;
output += available; output += available;
@@ -430,7 +443,7 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2
} }
else else
{ {
memcpy(output, static_cast<const uint8_t*>(bufferData) + m_firstBufferSamplesConsumed, sz); memcpy(output, bufferData + m_firstBufferSamplesConsumed, sz * sizeof(AudioSample_t));
m_firstBufferSamplesConsumed += sz; m_firstBufferSamplesConsumed += sz;
output += sz; output += sz;
sz = 0; sz = 0;
@@ -442,7 +455,7 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2
m_mutex->Unlock(); m_mutex->Unlock();
memset(output, 0x80, sz); memset(output, 0, sz * sizeof(AudioSample_t));
} }
GpAudioChannel_SDL2 *GpAudioChannel_SDL2::Alloc(GpAudioDriver_SDL2 *driver, GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate) GpAudioChannel_SDL2 *GpAudioChannel_SDL2::Alloc(GpAudioDriver_SDL2 *driver, GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate)
@@ -473,14 +486,14 @@ GpAudioDriver_SDL2::GpAudioDriver_SDL2(const GpAudioDriverProperties &properties
, m_latency(GpAudioDriver_SDL2_Duration_t::zero()) , m_latency(GpAudioDriver_SDL2_Duration_t::zero())
, m_bufferTime(GpAudioDriver_SDL2_Duration_t::zero()) , m_bufferTime(GpAudioDriver_SDL2_Duration_t::zero())
, m_sdlAudioRunning(false) , m_sdlAudioRunning(false)
, m_mixChunkReadOffset(kMixChunkSize) , m_mixChunkReadOffset(kMixChunkSamples)
, m_audioVolumeScale(kMaxAudioVolumeScale) , m_audioVolumeScale(kMaxAudioVolumeScale)
{ {
for (size_t i = 0; i < kMaxChannels; i++) for (size_t i = 0; i < kMaxChannels; i++)
m_channels[i] = nullptr; m_channels[i] = nullptr;
for (size_t i = 0; i < kMixChunkSize; i++) for (size_t i = 0; i < kMixChunkSamples; i++)
m_mixChunk[i] = 0; m_mixChunk[i] = 0;
} }
@@ -560,7 +573,7 @@ bool GpAudioDriver_SDL2::Init()
requestedSpec.channels = 1; requestedSpec.channels = 1;
requestedSpec.format = AUDIO_S16; requestedSpec.format = AUDIO_S16;
requestedSpec.freq = m_properties.m_sampleRate; requestedSpec.freq = m_properties.m_sampleRate;
requestedSpec.samples = kMixChunkSize; requestedSpec.samples = kMixChunkSamples;
requestedSpec.userdata = this; requestedSpec.userdata = this;
if (SDL_OpenAudio(&requestedSpec, nullptr)) if (SDL_OpenAudio(&requestedSpec, nullptr))
@@ -628,7 +641,7 @@ void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
size_t samplesSinceStart = 0; size_t samplesSinceStart = 0;
for (;;) for (;;)
{ {
size_t availableInMixChunk = kMixChunkSize - m_mixChunkReadOffset; size_t availableInMixChunk = kMixChunkSamples - m_mixChunkReadOffset;
if (availableInMixChunk > samplesRemaining) if (availableInMixChunk > samplesRemaining)
{ {
@@ -662,20 +675,21 @@ void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels, size_t maxSamplesToFill, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime) void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels, size_t maxSamplesToFill, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime)
{ {
uint8_t audioMixBufferUnaligned[kMixChunkSize + GP_SYSTEM_MEMORY_ALIGNMENT]; uint8_t audioMixBufferUnaligned[kMixChunkSamples * sizeof(AudioSample_t) + GP_SYSTEM_MEMORY_ALIGNMENT];
uint8_t *audioMixBuffer = audioMixBufferUnaligned; uint8_t *audioMixBufferBytes = audioMixBufferUnaligned;
{ {
uintptr_t bufferPtr = reinterpret_cast<uintptr_t>(audioMixBuffer); uintptr_t bufferPtr = reinterpret_cast<uintptr_t>(audioMixBufferBytes);
size_t alignPadding = GP_SYSTEM_MEMORY_ALIGNMENT - (bufferPtr % GP_SYSTEM_MEMORY_ALIGNMENT); size_t alignPadding = GP_SYSTEM_MEMORY_ALIGNMENT - (bufferPtr % GP_SYSTEM_MEMORY_ALIGNMENT);
audioMixBuffer += alignPadding; audioMixBufferBytes += alignPadding;
} }
AudioSample_t *audioMixBuffer = reinterpret_cast<AudioSample_t*>(audioMixBufferBytes);
bool noAudio = true; bool noAudio = true;
const int16_t audioVolumeScale = m_audioVolumeScale; const int16_t audioVolumeScale = m_audioVolumeScale;
size_t samplesToFill = kMixChunkSize; size_t samplesToFill = kMixChunkSamples;
if (samplesToFill > maxSamplesToFill) if (samplesToFill > maxSamplesToFill)
{ {
m_mixChunkReadOffset += samplesToFill - maxSamplesToFill; m_mixChunkReadOffset += samplesToFill - maxSamplesToFill;
@@ -684,8 +698,7 @@ void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, siz
else else
m_mixChunkReadOffset = 0; m_mixChunkReadOffset = 0;
int16_t *mixChunkStart = m_mixChunk + m_mixChunkReadOffset; AudioSample_t *mixChunkStart = m_mixChunk + m_mixChunkReadOffset;
int16_t audioNormalizeShift = 0;
for (size_t i = 0; i < numChannels; i++) for (size_t i = 0; i < numChannels; i++)
{ {
@@ -694,27 +707,27 @@ void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, siz
if (i == 0) if (i == 0)
{ {
noAudio = false; noAudio = false;
audioNormalizeShift = 0x80; memcpy(mixChunkStart, audioMixBuffer, samplesToFill * sizeof(AudioSample_t));
for (size_t j = 0; j < samplesToFill; j++)
mixChunkStart[j] = static_cast<int16_t>(audioMixBuffer[j]);
} }
else else
{ AddSamples(mixChunkStart, audioMixBuffer, samplesToFill);
audioNormalizeShift += 0x80;
for (size_t j = 0; j < samplesToFill; j++)
mixChunkStart[j] += static_cast<int16_t>(audioMixBuffer[j]);
}
} }
if (noAudio) if (noAudio)
memset(mixChunkStart, 0, samplesToFill * sizeof(mixChunkStart[0])); memset(mixChunkStart, 0, samplesToFill * sizeof(AudioSample_t));
else else
{ {
for (size_t i = 0; i < samplesToFill; i++) for (size_t i = 0; i < samplesToFill; i++)
mixChunkStart[i] = (mixChunkStart[i] - audioNormalizeShift) * audioVolumeScale; mixChunkStart[i] *= audioVolumeScale;
} }
} }
void GpAudioDriver_SDL2::AddSamples(AudioSample_t *GP_RESTRICT dest, const AudioSample_t *GP_RESTRICT src, size_t nSamples)
{
for (size_t i = 0; i < nSamples; i++)
dest[i] += src[i];
}
GpAudioDriver_SDL2_TimePoint_t GpAudioDriver_SDL2::GetCurrentTime() GpAudioDriver_SDL2_TimePoint_t GpAudioDriver_SDL2::GetCurrentTime()
{ {

View File

@@ -13,11 +13,13 @@
#define GP_ALIGNED(n) __declspec(align(n)) #define GP_ALIGNED(n) __declspec(align(n))
#else #else
#define GP_ALIGNED(n) __attribute__((aligned(n))) #define GP_ALIGNED(n) __attribute__((aligned(n)))
#define GP_RESTRICT
#endif #endif
#if GP_IS_CPP11 #if GP_IS_CPP11
#define GP_DELETED = delete #define GP_DELETED = delete
#define GP_STATIC_ASSERT(n) static_assert((n), "Static assert failed: " #n) #define GP_STATIC_ASSERT(n) static_assert((n), "Static assert failed: " #n)
#define GP_RESTRICT __restrict
#else #else
#ifndef nullptr #ifndef nullptr
#define nullptr 0 #define nullptr 0
@@ -32,6 +34,7 @@
#endif #endif
#define GP_DELETED #define GP_DELETED
#define GP_RESTRICT
template<bool TCondition> template<bool TCondition>
struct __GpStaticAssertHelper struct __GpStaticAssertHelper