Sweden-Number/libs/faudio/src/FAudio_internal.c

1968 lines
47 KiB
C

/* FAudio - XAudio Reimplementation for FNA
*
* Copyright (c) 2011-2021 Ethan Lee, Luigi Auriemma, and the MonoGame Team
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in a
* product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
* Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
*
*/
#include "FAudio_internal.h"
#ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
void WINAPIV FAudio_INTERNAL_debug(
FAudio *audio,
const char *file,
uint32_t line,
const char *func,
const char *fmt,
...
) {
char output[1024];
char *out = output;
va_list va;
out[0] = '\0';
/* Logging extras */
if (audio->debug.LogThreadID)
{
out += FAudio_snprintf(
out,
sizeof(output) - (out - output),
"0x%" FAudio_PRIx64 " ",
FAudio_PlatformGetThreadID()
);
}
if (audio->debug.LogFileline)
{
out += FAudio_snprintf(
out,
sizeof(output) - (out - output),
"%s:%u ",
file,
line
);
}
if (audio->debug.LogFunctionName)
{
out += FAudio_snprintf(
out,
sizeof(output) - (out - output),
"%s ",
func
);
}
if (audio->debug.LogTiming)
{
out += FAudio_snprintf(
out,
sizeof(output) - (out - output),
"%dms ",
FAudio_timems()
);
}
/* The actual message... */
va_start(va, fmt);
FAudio_vsnprintf(
out,
sizeof(output) - (out - output),
fmt,
va
);
va_end(va);
/* Print, finally. */
FAudio_Log(output);
}
static const char *get_wformattag_string(const FAudioWaveFormatEx *fmt)
{
#define FMT_STRING(suffix) \
if (fmt->wFormatTag == FAUDIO_FORMAT_##suffix) \
{ \
return #suffix; \
}
FMT_STRING(PCM)
FMT_STRING(MSADPCM)
FMT_STRING(IEEE_FLOAT)
FMT_STRING(XMAUDIO2)
FMT_STRING(WMAUDIO2)
FMT_STRING(EXTENSIBLE)
#undef FMT_STRING
return "UNKNOWN!";
}
static const char *get_subformat_string(const FAudioWaveFormatEx *fmt)
{
const FAudioWaveFormatExtensible *fmtex = (const FAudioWaveFormatExtensible*) fmt;
if (fmt->wFormatTag != FAUDIO_FORMAT_EXTENSIBLE)
{
return "N/A";
}
if (!FAudio_memcmp(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID)))
{
return "IEEE_FLOAT";
}
if (!FAudio_memcmp(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_PCM, sizeof(FAudioGUID)))
{
return "PCM";
}
return "UNKNOWN!";
}
void FAudio_INTERNAL_debug_fmt(
FAudio *audio,
const char *file,
uint32_t line,
const char *func,
const FAudioWaveFormatEx *fmt
) {
FAudio_INTERNAL_debug(
audio,
file,
line,
func,
(
"{"
"wFormatTag: 0x%x %s, "
"nChannels: %u, "
"nSamplesPerSec: %u, "
"wBitsPerSample: %u, "
"nBlockAlign: %u, "
"SubFormat: %s"
"}"
),
fmt->wFormatTag,
get_wformattag_string(fmt),
fmt->nChannels,
fmt->nSamplesPerSec,
fmt->wBitsPerSample,
fmt->nBlockAlign,
get_subformat_string(fmt)
);
}
#endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
void LinkedList_AddEntry(
LinkedList **start,
void* toAdd,
FAudioMutex lock,
FAudioMallocFunc pMalloc
) {
LinkedList *newEntry, *latest;
newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
newEntry->entry = toAdd;
newEntry->next = NULL;
FAudio_PlatformLockMutex(lock);
if (*start == NULL)
{
*start = newEntry;
}
else
{
latest = *start;
while (latest->next != NULL)
{
latest = latest->next;
}
latest->next = newEntry;
}
FAudio_PlatformUnlockMutex(lock);
}
void LinkedList_PrependEntry(
LinkedList **start,
void* toAdd,
FAudioMutex lock,
FAudioMallocFunc pMalloc
) {
LinkedList *newEntry;
newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
newEntry->entry = toAdd;
FAudio_PlatformLockMutex(lock);
newEntry->next = *start;
*start = newEntry;
FAudio_PlatformUnlockMutex(lock);
}
void LinkedList_RemoveEntry(
LinkedList **start,
void* toRemove,
FAudioMutex lock,
FAudioFreeFunc pFree
) {
LinkedList *latest, *prev;
latest = *start;
prev = latest;
FAudio_PlatformLockMutex(lock);
while (latest != NULL)
{
if (latest->entry == toRemove)
{
if (latest == prev) /* First in list */
{
*start = latest->next;
}
else
{
prev->next = latest->next;
}
pFree(latest);
FAudio_PlatformUnlockMutex(lock);
return;
}
prev = latest;
latest = latest->next;
}
FAudio_PlatformUnlockMutex(lock);
FAudio_assert(0 && "LinkedList element not found!");
}
void FAudio_INTERNAL_InsertSubmixSorted(
LinkedList **start,
FAudioSubmixVoice *toAdd,
FAudioMutex lock,
FAudioMallocFunc pMalloc
) {
LinkedList *newEntry, *latest;
newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
newEntry->entry = toAdd;
newEntry->next = NULL;
FAudio_PlatformLockMutex(lock);
if (*start == NULL)
{
*start = newEntry;
}
else
{
latest = *start;
/* Special case if the new stage is lower than everyone else */
if (toAdd->mix.processingStage < ((FAudioSubmixVoice*) latest->entry)->mix.processingStage)
{
newEntry->next = latest;
*start = newEntry;
}
else
{
/* If we got here, we know that the new stage is
* _at least_ as high as the first submix in the list.
*
* Each loop iteration checks to see if the new stage
* is smaller than `latest->next`, meaning it fits
* between `latest` and `latest->next`.
*/
while (latest->next != NULL)
{
if (toAdd->mix.processingStage < ((FAudioSubmixVoice *) latest->next->entry)->mix.processingStage)
{
newEntry->next = latest->next;
latest->next = newEntry;
break;
}
latest = latest->next;
}
/* If newEntry didn't get a `next` value, that means
* it didn't fall in between any stages and `latest`
* is the last entry in the list. Add it to the end!
*/
if (newEntry->next == NULL)
{
latest->next = newEntry;
}
}
}
FAudio_PlatformUnlockMutex(lock);
}
static uint32_t FAudio_INTERNAL_GetBytesRequested(
FAudioSourceVoice *voice,
uint32_t decoding
) {
uint32_t end, result;
FAudioBuffer *buffer;
FAudioWaveFormatExtensible *fmt;
FAudioBufferEntry *list = voice->src.bufferList;
LOG_FUNC_ENTER(voice->audio)
#ifdef HAVE_WMADEC
if (voice->src.wmadec != NULL)
{
/* Always 0, per the spec */
LOG_FUNC_EXIT(voice->audio)
return 0;
}
#endif /* HAVE_WMADEC */
while (list != NULL && decoding > 0)
{
buffer = &list->buffer;
if (buffer->LoopCount > 0)
{
end = (
/* Current loop... */
((buffer->LoopBegin + buffer->LoopLength) - voice->src.curBufferOffset) +
/* Remaining loops... */
(buffer->LoopLength * buffer->LoopCount - 1) +
/* ... Final iteration */
buffer->PlayLength
);
}
else
{
end = (buffer->PlayBegin + buffer->PlayLength) - voice->src.curBufferOffset;
}
if (end > decoding)
{
decoding = 0;
break;
}
decoding -= end;
list = list->next;
}
/* Convert samples to bytes, factoring block alignment */
if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
{
fmt = (FAudioWaveFormatExtensible*) voice->src.format;
result = (
(decoding / fmt->Samples.wSamplesPerBlock) +
((decoding % fmt->Samples.wSamplesPerBlock) > 0)
) * voice->src.format->nBlockAlign;
}
else
{
result = decoding * voice->src.format->nBlockAlign;
}
LOG_FUNC_EXIT(voice->audio)
return result;
}
static void FAudio_INTERNAL_DecodeBuffers(
FAudioSourceVoice *voice,
uint64_t *toDecode
) {
uint32_t end, endRead, decoding, decoded = 0;
FAudioBuffer *buffer = &voice->src.bufferList->buffer;
FAudioBufferEntry *toDelete;
LOG_FUNC_ENTER(voice->audio)
/* This should never go past the max ratio size */
FAudio_assert(*toDecode <= voice->src.decodeSamples);
while (decoded < *toDecode && buffer != NULL)
{
decoding = (uint32_t) *toDecode - decoded;
/* Start-of-buffer behavior */
if (voice->src.newBuffer)
{
voice->src.newBuffer = 0;
if ( voice->src.callback != NULL &&
voice->src.callback->OnBufferStart != NULL )
{
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
voice->src.callback->OnBufferStart(
voice->src.callback,
buffer->pContext
);
FAudio_PlatformLockMutex(voice->audio->sourceLock);
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
}
}
/* Check for end-of-buffer */
end = (buffer->LoopCount > 0) ?
(buffer->LoopBegin + buffer->LoopLength) :
buffer->PlayBegin + buffer->PlayLength;
endRead = FAudio_min(
end - voice->src.curBufferOffset,
decoding
);
/* Decode... */
voice->src.decode(
voice,
buffer,
voice->audio->decodeCache + (
decoded * voice->src.format->nChannels
),
endRead
);
LOG_INFO(
voice->audio,
"Voice %p, buffer %p, decoded %u samples from [%u,%u)",
(void*) voice,
(void*) buffer,
endRead,
voice->src.curBufferOffset,
voice->src.curBufferOffset + endRead
)
decoded += endRead;
voice->src.curBufferOffset += endRead;
voice->src.totalSamples += endRead;
/* End-of-buffer behavior */
if (endRead < decoding)
{
if (buffer->LoopCount > 0)
{
voice->src.curBufferOffset = buffer->LoopBegin;
if (buffer->LoopCount < FAUDIO_LOOP_INFINITE)
{
buffer->LoopCount -= 1;
}
if ( voice->src.callback != NULL &&
voice->src.callback->OnLoopEnd != NULL )
{
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
voice->src.callback->OnLoopEnd(
voice->src.callback,
buffer->pContext
);
FAudio_PlatformLockMutex(voice->audio->sourceLock);
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
}
}
else
{
#ifdef HAVE_WMADEC
if (voice->src.wmadec != NULL)
{
FAudio_WMADEC_end_buffer(voice);
}
#endif /* HAVE_WMADEC */
/* For EOS we can stop storing fraction offsets */
if (buffer->Flags & FAUDIO_END_OF_STREAM)
{
voice->src.curBufferOffsetDec = 0;
voice->src.totalSamples = 0;
}
LOG_INFO(
voice->audio,
"Voice %p, finished with buffer %p",
(void*) voice,
(void*) buffer
)
/* Change active buffer, delete finished buffer */
toDelete = voice->src.bufferList;
voice->src.bufferList = voice->src.bufferList->next;
if (voice->src.bufferList != NULL)
{
buffer = &voice->src.bufferList->buffer;
voice->src.curBufferOffset = buffer->PlayBegin;
}
else
{
buffer = NULL;
/* FIXME: I keep going past the buffer so fuck it */
FAudio_zero(
voice->audio->decodeCache + (
decoded *
voice->src.format->nChannels
),
sizeof(float) * (
(*toDecode - decoded) *
voice->src.format->nChannels
)
);
}
/* Callbacks */
if (voice->src.callback != NULL)
{
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
if (voice->src.callback->OnBufferEnd != NULL)
{
voice->src.callback->OnBufferEnd(
voice->src.callback,
toDelete->buffer.pContext
);
}
if ( toDelete->buffer.Flags & FAUDIO_END_OF_STREAM &&
voice->src.callback->OnStreamEnd != NULL )
{
voice->src.callback->OnStreamEnd(
voice->src.callback
);
}
/* One last chance at redemption */
if (buffer == NULL && voice->src.bufferList != NULL)
{
buffer = &voice->src.bufferList->buffer;
voice->src.curBufferOffset = buffer->PlayBegin;
}
if (buffer != NULL && voice->src.callback->OnBufferStart != NULL)
{
voice->src.callback->OnBufferStart(
voice->src.callback,
buffer->pContext
);
}
FAudio_PlatformLockMutex(voice->audio->sourceLock);
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
}
voice->audio->pFree(toDelete);
}
}
}
/* ... FIXME: I keep going past the buffer so fuck it */
if (buffer)
{
end = (buffer->LoopCount > 0) ?
(buffer->LoopBegin + buffer->LoopLength) :
buffer->PlayBegin + buffer->PlayLength;
endRead = FAudio_min(
end - voice->src.curBufferOffset,
EXTRA_DECODE_PADDING
);
voice->src.decode(
voice,
buffer,
voice->audio->decodeCache + (
decoded * voice->src.format->nChannels
),
endRead
);
/* Do NOT increment curBufferOffset! */
if (endRead < EXTRA_DECODE_PADDING)
{
FAudio_zero(
voice->audio->decodeCache + (
decoded * voice->src.format->nChannels
),
sizeof(float) * (
(EXTRA_DECODE_PADDING - endRead) *
voice->src.format->nChannels
)
);
}
}
else
{
FAudio_zero(
voice->audio->decodeCache + (
decoded * voice->src.format->nChannels
),
sizeof(float) * (
EXTRA_DECODE_PADDING *
voice->src.format->nChannels
)
);
}
*toDecode = decoded;
LOG_FUNC_EXIT(voice->audio)
}
static inline void FAudio_INTERNAL_FilterVoice(
FAudio *audio,
const FAudioFilterParameters *filter,
FAudioFilterState *filterState,
float *samples,
uint32_t numSamples,
uint16_t numChannels
) {
uint32_t j, ci;
LOG_FUNC_ENTER(audio)
/* Apply a digital state-variable filter to the voice.
* The difference equations of the filter are:
*
* Yl(n) = F Yb(n - 1) + Yl(n - 1)
* Yh(n) = x(n) - Yl(n) - OneOverQ Yb(n - 1)
* Yb(n) = F Yh(n) + Yb(n - 1)
* Yn(n) = Yl(n) + Yh(n)
*
* Please note that FAudioFilterParameters.Frequency is defined as:
*
* (2 * sin(pi * (desired filter cutoff frequency) / sampleRate))
*
* - @JohanSmet
*/
for (j = 0; j < numSamples; j += 1)
for (ci = 0; ci < numChannels; ci += 1)
{
filterState[ci][FAudioLowPassFilter] = filterState[ci][FAudioLowPassFilter] + (filter->Frequency * filterState[ci][FAudioBandPassFilter]);
filterState[ci][FAudioHighPassFilter] = samples[j * numChannels + ci] - filterState[ci][FAudioLowPassFilter] - (filter->OneOverQ * filterState[ci][FAudioBandPassFilter]);
filterState[ci][FAudioBandPassFilter] = (filter->Frequency * filterState[ci][FAudioHighPassFilter]) + filterState[ci][FAudioBandPassFilter];
filterState[ci][FAudioNotchFilter] = filterState[ci][FAudioHighPassFilter] + filterState[ci][FAudioLowPassFilter];
samples[j * numChannels + ci] = filterState[ci][filter->Type];
}
LOG_FUNC_EXIT(audio)
}
static void FAudio_INTERNAL_ResizeEffectChainCache(FAudio *audio, uint32_t samples)
{
LOG_FUNC_ENTER(audio)
if (samples > audio->effectChainSamples)
{
audio->effectChainSamples = samples;
audio->effectChainCache = (float*) audio->pRealloc(
audio->effectChainCache,
sizeof(float) * audio->effectChainSamples
);
}
LOG_FUNC_EXIT(audio)
}
static inline float *FAudio_INTERNAL_ProcessEffectChain(
FAudioVoice *voice,
float *buffer,
uint32_t *samples
) {
uint32_t i;
FAPO *fapo;
FAPOProcessBufferParameters srcParams, dstParams;
LOG_FUNC_ENTER(voice->audio)
/* Set up the buffer to be written into */
srcParams.pBuffer = buffer;
srcParams.BufferFlags = FAPO_BUFFER_SILENT;
srcParams.ValidFrameCount = *samples;
for (i = 0; i < srcParams.ValidFrameCount; i += 1)
{
if (buffer[i] != 0.0f) /* Arbitrary! */
{
srcParams.BufferFlags = FAPO_BUFFER_VALID;
break;
}
}
/* Initialize output parameters to something sane */
dstParams.pBuffer = srcParams.pBuffer;
dstParams.BufferFlags = FAPO_BUFFER_VALID;
dstParams.ValidFrameCount = srcParams.ValidFrameCount;
/* Update parameters, process! */
for (i = 0; i < voice->effects.count; i += 1)
{
fapo = voice->effects.desc[i].pEffect;
if (!voice->effects.inPlaceProcessing[i])
{
if (dstParams.pBuffer == buffer)
{
FAudio_INTERNAL_ResizeEffectChainCache(
voice->audio,
voice->effects.desc[i].OutputChannels * srcParams.ValidFrameCount
);
dstParams.pBuffer = voice->audio->effectChainCache;
}
else
{
/* FIXME: What if this is smaller because
* inputChannels < desc[i].OutputChannels?
*/
dstParams.pBuffer = buffer;
}
FAudio_zero(
dstParams.pBuffer,
voice->effects.desc[i].OutputChannels * srcParams.ValidFrameCount * sizeof(float)
);
}
if (voice->effects.parameterUpdates[i])
{
fapo->SetParameters(
fapo,
voice->effects.parameters[i],
voice->effects.parameterSizes[i]
);
voice->effects.parameterUpdates[i] = 0;
}
fapo->Process(
fapo,
1,
&srcParams,
1,
&dstParams,
voice->effects.desc[i].InitialState
);
FAudio_memcpy(&srcParams, &dstParams, sizeof(dstParams));
}
*samples = dstParams.ValidFrameCount;
/* Save the output buffer-flags so the mixer-function can determine when it's save to stop processing the effect chain */
voice->effects.state = dstParams.BufferFlags;
LOG_FUNC_EXIT(voice->audio)
return (float*) dstParams.pBuffer;
}
static void FAudio_INTERNAL_ResizeResampleCache(FAudio *audio, uint32_t samples)
{
LOG_FUNC_ENTER(audio)
if (samples > audio->resampleSamples)
{
audio->resampleSamples = samples;
audio->resampleCache = (float*) audio->pRealloc(
audio->resampleCache,
sizeof(float) * audio->resampleSamples
);
}
LOG_FUNC_EXIT(audio)
}
static void FAudio_INTERNAL_MixSource(FAudioSourceVoice *voice)
{
/* Iterators */
uint32_t i;
/* Decode/Resample variables */
uint64_t toDecode;
uint64_t toResample;
/* Output mix variables */
float *stream;
uint32_t mixed;
uint32_t oChan;
FAudioVoice *out;
uint32_t outputRate;
double stepd;
float *finalSamples;
LOG_FUNC_ENTER(voice->audio)
FAudio_PlatformLockMutex(voice->sendLock);
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
/* Calculate the resample stepping value */
if (voice->src.resampleFreq != voice->src.freqRatio * voice->src.format->nSamplesPerSec)
{
out = (voice->sends.SendCount == 0) ?
voice->audio->master : /* Barf */
voice->sends.pSends->pOutputVoice;
outputRate = (out->type == FAUDIO_VOICE_MASTER) ?
out->master.inputSampleRate :
out->mix.inputSampleRate;
stepd = (
voice->src.freqRatio *
(double) voice->src.format->nSamplesPerSec /
(double) outputRate
);
voice->src.resampleStep = DOUBLE_TO_FIXED(stepd);
voice->src.resampleFreq = voice->src.freqRatio * voice->src.format->nSamplesPerSec;
}
if (voice->src.active == 2)
{
/* We're just playing tails, skip all buffer stuff */
FAudio_INTERNAL_ResizeResampleCache(
voice->audio,
voice->src.resampleSamples * voice->src.format->nChannels
);
mixed = voice->src.resampleSamples;
FAudio_zero(
voice->audio->resampleCache,
mixed * voice->src.format->nChannels * sizeof(float)
);
finalSamples = voice->audio->resampleCache;
goto sendwork;
}
/* Base decode size, int to fixed... */
toDecode = voice->src.resampleSamples * voice->src.resampleStep;
/* ... rounded up based on current offset... */
toDecode += voice->src.curBufferOffsetDec + FIXED_FRACTION_MASK;
/* ... fixed to int, truncating extra fraction from rounding. */
toDecode >>= FIXED_PRECISION;
/* First voice callback */
if ( voice->src.callback != NULL &&
voice->src.callback->OnVoiceProcessingPassStart != NULL )
{
FAudio_PlatformUnlockMutex(voice->sendLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
voice->src.callback->OnVoiceProcessingPassStart(
voice->src.callback,
FAudio_INTERNAL_GetBytesRequested(voice, (uint32_t) toDecode)
);
FAudio_PlatformLockMutex(voice->audio->sourceLock);
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
FAudio_PlatformLockMutex(voice->sendLock);
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
}
FAudio_PlatformLockMutex(voice->src.bufferLock);
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
/* Nothing to do? */
if (voice->src.bufferList == NULL)
{
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
if (voice->effects.count > 0 && voice->effects.state != FAPO_BUFFER_SILENT)
{
/* do not stop while the effect chain generates a non-silent buffer */
FAudio_INTERNAL_ResizeResampleCache(
voice->audio,
voice->src.resampleSamples * voice->src.format->nChannels
);
mixed = voice->src.resampleSamples;
FAudio_zero(
voice->audio->resampleCache,
mixed * voice->src.format->nChannels * sizeof(float)
);
finalSamples = voice->audio->resampleCache;
goto sendwork;
}
FAudio_PlatformUnlockMutex(voice->sendLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
if ( voice->src.callback != NULL &&
voice->src.callback->OnVoiceProcessingPassEnd != NULL)
{
voice->src.callback->OnVoiceProcessingPassEnd(
voice->src.callback
);
}
FAudio_PlatformLockMutex(voice->audio->sourceLock);
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
LOG_FUNC_EXIT(voice->audio)
return;
}
/* Decode... */
FAudio_INTERNAL_DecodeBuffers(voice, &toDecode);
/* Subtract any padding samples from the total, if applicable */
if ( voice->src.curBufferOffsetDec > 0 &&
voice->src.totalSamples > 0 )
{
voice->src.totalSamples -= 1;
}
/* Okay, we're done messing with client data */
if ( voice->src.callback != NULL &&
voice->src.callback->OnVoiceProcessingPassEnd != NULL)
{
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
FAudio_PlatformUnlockMutex(voice->sendLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
voice->src.callback->OnVoiceProcessingPassEnd(
voice->src.callback
);
FAudio_PlatformLockMutex(voice->audio->sourceLock);
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
FAudio_PlatformLockMutex(voice->sendLock);
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
FAudio_PlatformLockMutex(voice->src.bufferLock);
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
}
/* Nothing to resample? */
if (toDecode == 0)
{
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
FAudio_PlatformUnlockMutex(voice->sendLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
LOG_FUNC_EXIT(voice->audio)
return;
}
/* int to fixed... */
toResample = toDecode << FIXED_PRECISION;
/* ... round back down based on current offset... */
toResample -= voice->src.curBufferOffsetDec;
/* ... but also ceil for any fraction value... */
toResample += FIXED_FRACTION_MASK;
/* ... undo step size, fixed to int. */
toResample /= voice->src.resampleStep;
/* Add the padding, for some reason this helps? */
toResample += EXTRA_DECODE_PADDING;
/* FIXME: I feel like this should be an assert but I suck */
toResample = FAudio_min(toResample, voice->src.resampleSamples);
/* Resample... */
if (voice->src.resampleStep == FIXED_ONE)
{
/* Actually, just use the existing buffer... */
finalSamples = voice->audio->decodeCache;
}
else
{
FAudio_INTERNAL_ResizeResampleCache(
voice->audio,
voice->src.resampleSamples * voice->src.format->nChannels
);
voice->src.resample(
voice->audio->decodeCache,
voice->audio->resampleCache,
&voice->src.resampleOffset,
voice->src.resampleStep,
toResample,
(uint8_t) voice->src.format->nChannels
);
finalSamples = voice->audio->resampleCache;
}
/* Update buffer offsets */
if (voice->src.bufferList != NULL)
{
/* Increment fixed offset by resample size, int to fixed... */
voice->src.curBufferOffsetDec += toResample * voice->src.resampleStep;
/* ... chop off any ints we got from the above increment */
voice->src.curBufferOffsetDec &= FIXED_FRACTION_MASK;
/* Dec >0? We need one frame from the past...
* FIXME: We can't go back to a prev buffer though?
*/
if ( voice->src.curBufferOffsetDec > 0 &&
voice->src.curBufferOffset > 0 )
{
voice->src.curBufferOffset -= 1;
}
}
else
{
voice->src.curBufferOffsetDec = 0;
voice->src.curBufferOffset = 0;
}
/* Done with buffers, finally. */
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
mixed = (uint32_t) toResample;
sendwork:
/* Filters */
if (voice->flags & FAUDIO_VOICE_USEFILTER)
{
FAudio_PlatformLockMutex(voice->filterLock);
LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
FAudio_INTERNAL_FilterVoice(
voice->audio,
&voice->filter,
voice->filterState,
finalSamples,
mixed,
voice->src.format->nChannels
);
FAudio_PlatformUnlockMutex(voice->filterLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
}
/* Process effect chain */
FAudio_PlatformLockMutex(voice->effectLock);
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
if (voice->effects.count > 0)
{
/* If we didn't get the full size of the update, we have to fill
* it with silence so the effect can process a whole update
*/
if (mixed < voice->src.resampleSamples)
{
FAudio_zero(
finalSamples + (mixed * voice->src.format->nChannels),
(voice->src.resampleSamples - mixed) * voice->src.format->nChannels * sizeof(float)
);
mixed = voice->src.resampleSamples;
}
finalSamples = FAudio_INTERNAL_ProcessEffectChain(
voice,
finalSamples,
&mixed
);
}
FAudio_PlatformUnlockMutex(voice->effectLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
/* Nowhere to send it? Just skip the rest...*/
if (voice->sends.SendCount == 0)
{
FAudio_PlatformUnlockMutex(voice->sendLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
LOG_FUNC_EXIT(voice->audio)
return;
}
/* Send float cache to sends */
FAudio_PlatformLockMutex(voice->volumeLock);
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
for (i = 0; i < voice->sends.SendCount; i += 1)
{
out = voice->sends.pSends[i].pOutputVoice;
if (out->type == FAUDIO_VOICE_MASTER)
{
stream = out->master.output;
oChan = out->master.inputChannels;
}
else
{
stream = out->mix.inputCache;
oChan = out->mix.inputChannels;
}
voice->sendMix[i](
mixed,
voice->outputChannels,
oChan,
finalSamples,
stream,
voice->mixCoefficients[i]
);
if (voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER)
{
FAudio_INTERNAL_FilterVoice(
voice->audio,
&voice->sendFilter[i],
voice->sendFilterState[i],
stream,
mixed,
oChan
);
}
}
FAudio_PlatformUnlockMutex(voice->volumeLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
FAudio_PlatformUnlockMutex(voice->sendLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
LOG_FUNC_EXIT(voice->audio)
}
static void FAudio_INTERNAL_MixSubmix(FAudioSubmixVoice *voice)
{
uint32_t i;
float *stream;
uint32_t oChan;
FAudioVoice *out;
uint32_t resampled;
uint64_t resampleOffset = 0;
float *finalSamples;
LOG_FUNC_ENTER(voice->audio)
FAudio_PlatformLockMutex(voice->sendLock);
LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
/* Resample */
if (voice->mix.resampleStep == FIXED_ONE)
{
/* Actually, just use the existing buffer... */
finalSamples = voice->mix.inputCache;
}
else
{
FAudio_INTERNAL_ResizeResampleCache(
voice->audio,
voice->mix.outputSamples * voice->mix.inputChannels
);
voice->mix.resample(
voice->mix.inputCache,
voice->audio->resampleCache,
&resampleOffset,
voice->mix.resampleStep,
voice->mix.outputSamples,
(uint8_t) voice->mix.inputChannels
);
finalSamples = voice->audio->resampleCache;
}
resampled = voice->mix.outputSamples * voice->mix.inputChannels;
/* Submix overall volume is applied _before_ effects/filters, blech! */
if (voice->volume != 1.0f)
{
FAudio_INTERNAL_Amplify(
finalSamples,
resampled,
voice->volume
);
}
resampled /= voice->mix.inputChannels;
/* Filters */
if (voice->flags & FAUDIO_VOICE_USEFILTER)
{
FAudio_PlatformLockMutex(voice->filterLock);
LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
FAudio_INTERNAL_FilterVoice(
voice->audio,
&voice->filter,
voice->filterState,
finalSamples,
resampled,
voice->mix.inputChannels
);
FAudio_PlatformUnlockMutex(voice->filterLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
}
/* Process effect chain */
FAudio_PlatformLockMutex(voice->effectLock);
LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
if (voice->effects.count > 0)
{
finalSamples = FAudio_INTERNAL_ProcessEffectChain(
voice,
finalSamples,
&resampled
);
}
FAudio_PlatformUnlockMutex(voice->effectLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
/* Nothing more to do? */
if (voice->sends.SendCount == 0)
{
goto end;
}
/* Send float cache to sends */
FAudio_PlatformLockMutex(voice->volumeLock);
LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
for (i = 0; i < voice->sends.SendCount; i += 1)
{
out = voice->sends.pSends[i].pOutputVoice;
if (out->type == FAUDIO_VOICE_MASTER)
{
stream = out->master.output;
oChan = out->master.inputChannels;
}
else
{
stream = out->mix.inputCache;
oChan = out->mix.inputChannels;
}
voice->sendMix[i](
resampled,
voice->outputChannels,
oChan,
finalSamples,
stream,
voice->mixCoefficients[i]
);
if (voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER)
{
FAudio_INTERNAL_FilterVoice(
voice->audio,
&voice->sendFilter[i],
voice->sendFilterState[i],
stream,
resampled,
oChan
);
}
}
FAudio_PlatformUnlockMutex(voice->volumeLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
/* Zero this at the end, for the next update */
end:
FAudio_PlatformUnlockMutex(voice->sendLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
FAudio_zero(
voice->mix.inputCache,
sizeof(float) * voice->mix.inputSamples
);
LOG_FUNC_EXIT(voice->audio)
}
static void FAudio_INTERNAL_FlushPendingBuffers(FAudioSourceVoice *voice)
{
FAudioBufferEntry *entry;
FAudio_PlatformLockMutex(voice->src.bufferLock);
LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
/* Remove pending flushed buffers and send an event for each one */
while (voice->src.flushList != NULL)
{
entry = voice->src.flushList;
voice->src.flushList = voice->src.flushList->next;
if (voice->src.callback != NULL && voice->src.callback->OnBufferEnd != NULL)
{
FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
voice->src.callback->OnBufferEnd(
voice->src.callback,
entry->buffer.pContext
);
FAudio_PlatformLockMutex(voice->audio->sourceLock);
LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
}
voice->audio->pFree(entry);
}
FAudio_PlatformUnlockMutex(voice->src.bufferLock);
LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
}
static void FAUDIOCALL FAudio_INTERNAL_GenerateOutput(FAudio *audio, float *output)
{
uint32_t totalSamples;
LinkedList *list;
float *effectOut;
FAudioEngineCallback *callback;
LOG_FUNC_ENTER(audio)
if (!audio->active)
{
LOG_FUNC_EXIT(audio)
return;
}
/* Apply any committed changes */
FAudio_OPERATIONSET_Execute(audio);
/* ProcessingPassStart callbacks */
FAudio_PlatformLockMutex(audio->callbackLock);
LOG_MUTEX_LOCK(audio, audio->callbackLock)
list = audio->callbacks;
while (list != NULL)
{
callback = (FAudioEngineCallback*) list->entry;
if (callback->OnProcessingPassStart != NULL)
{
callback->OnProcessingPassStart(
callback
);
}
list = list->next;
}
FAudio_PlatformUnlockMutex(audio->callbackLock);
LOG_MUTEX_UNLOCK(audio, audio->callbackLock)
/* Writes to master will directly write to output, but ONLY if there
* isn't any channel-changing effect processing to do first.
*/
if (audio->master->master.effectCache != NULL)
{
audio->master->master.output = audio->master->master.effectCache;
FAudio_zero(
audio->master->master.effectCache,
(
sizeof(float) *
audio->updateSize *
audio->master->master.inputChannels
)
);
}
else
{
audio->master->master.output = output;
}
/* Mix sources */
FAudio_PlatformLockMutex(audio->sourceLock);
LOG_MUTEX_LOCK(audio, audio->sourceLock)
list = audio->sources;
while (list != NULL)
{
audio->processingSource = (FAudioSourceVoice*) list->entry;
FAudio_INTERNAL_FlushPendingBuffers(audio->processingSource);
if (audio->processingSource->src.active)
{
FAudio_INTERNAL_MixSource(audio->processingSource);
FAudio_INTERNAL_FlushPendingBuffers(audio->processingSource);
}
list = list->next;
}
audio->processingSource = NULL;
FAudio_PlatformUnlockMutex(audio->sourceLock);
LOG_MUTEX_UNLOCK(audio, audio->sourceLock)
/* Mix submixes, ordered by processing stage */
FAudio_PlatformLockMutex(audio->submixLock);
LOG_MUTEX_LOCK(audio, audio->submixLock)
list = audio->submixes;
while (list != NULL)
{
FAudio_INTERNAL_MixSubmix((FAudioSubmixVoice*) list->entry);
list = list->next;
}
FAudio_PlatformUnlockMutex(audio->submixLock);
LOG_MUTEX_UNLOCK(audio, audio->submixLock)
/* Apply master volume */
if (audio->master->volume != 1.0f)
{
FAudio_INTERNAL_Amplify(
audio->master->master.output,
audio->updateSize * audio->master->master.inputChannels,
audio->master->volume
);
}
/* Process master effect chain */
FAudio_PlatformLockMutex(audio->master->effectLock);
LOG_MUTEX_LOCK(audio, audio->master->effectLock)
if (audio->master->effects.count > 0)
{
totalSamples = audio->updateSize;
effectOut = FAudio_INTERNAL_ProcessEffectChain(
audio->master,
audio->master->master.output,
&totalSamples
);
if (effectOut != output)
{
FAudio_memcpy(
output,
effectOut,
totalSamples * audio->master->outputChannels * sizeof(float)
);
}
if (totalSamples < audio->updateSize)
{
FAudio_zero(
output + (totalSamples * audio->master->outputChannels),
(audio->updateSize - totalSamples) * sizeof(float)
);
}
}
FAudio_PlatformUnlockMutex(audio->master->effectLock);
LOG_MUTEX_UNLOCK(audio, audio->master->effectLock)
/* OnProcessingPassEnd callbacks */
FAudio_PlatformLockMutex(audio->callbackLock);
LOG_MUTEX_LOCK(audio, audio->callbackLock)
list = audio->callbacks;
while (list != NULL)
{
callback = (FAudioEngineCallback*) list->entry;
if (callback->OnProcessingPassEnd != NULL)
{
callback->OnProcessingPassEnd(
callback
);
}
list = list->next;
}
FAudio_PlatformUnlockMutex(audio->callbackLock);
LOG_MUTEX_UNLOCK(audio, audio->callbackLock)
LOG_FUNC_EXIT(audio)
}
void FAudio_INTERNAL_UpdateEngine(FAudio *audio, float *output)
{
LOG_FUNC_ENTER(audio)
if (audio->pClientEngineProc)
{
audio->pClientEngineProc(
&FAudio_INTERNAL_GenerateOutput,
audio,
output,
audio->clientEngineUser
);
}
else
{
FAudio_INTERNAL_GenerateOutput(audio, output);
}
LOG_FUNC_EXIT(audio)
}
void FAudio_INTERNAL_ResizeDecodeCache(FAudio *audio, uint32_t samples)
{
LOG_FUNC_ENTER(audio)
FAudio_PlatformLockMutex(audio->sourceLock);
LOG_MUTEX_LOCK(audio, audio->sourceLock)
if (samples > audio->decodeSamples)
{
audio->decodeSamples = samples;
audio->decodeCache = (float*) audio->pRealloc(
audio->decodeCache,
sizeof(float) * audio->decodeSamples
);
}
FAudio_PlatformUnlockMutex(audio->sourceLock);
LOG_MUTEX_UNLOCK(audio, audio->sourceLock)
LOG_FUNC_EXIT(audio)
}
void FAudio_INTERNAL_AllocEffectChain(
FAudioVoice *voice,
const FAudioEffectChain *pEffectChain
) {
uint32_t i;
LOG_FUNC_ENTER(voice->audio)
voice->effects.state = FAPO_BUFFER_VALID;
voice->effects.count = pEffectChain->EffectCount;
if (voice->effects.count == 0)
{
LOG_FUNC_EXIT(voice->audio)
return;
}
for (i = 0; i < pEffectChain->EffectCount; i += 1)
{
pEffectChain->pEffectDescriptors[i].pEffect->AddRef(pEffectChain->pEffectDescriptors[i].pEffect);
}
voice->effects.desc = (FAudioEffectDescriptor*) voice->audio->pMalloc(
voice->effects.count * sizeof(FAudioEffectDescriptor)
);
FAudio_memcpy(
voice->effects.desc,
pEffectChain->pEffectDescriptors,
voice->effects.count * sizeof(FAudioEffectDescriptor)
);
#define ALLOC_EFFECT_PROPERTY(prop, type) \
voice->effects.prop = (type*) voice->audio->pMalloc( \
voice->effects.count * sizeof(type) \
); \
FAudio_zero( \
voice->effects.prop, \
voice->effects.count * sizeof(type) \
);
ALLOC_EFFECT_PROPERTY(parameters, void*)
ALLOC_EFFECT_PROPERTY(parameterSizes, uint32_t)
ALLOC_EFFECT_PROPERTY(parameterUpdates, uint8_t)
ALLOC_EFFECT_PROPERTY(inPlaceProcessing, uint8_t)
#undef ALLOC_EFFECT_PROPERTY
LOG_FUNC_EXIT(voice->audio)
}
void FAudio_INTERNAL_FreeEffectChain(FAudioVoice *voice)
{
uint32_t i;
LOG_FUNC_ENTER(voice->audio)
if (voice->effects.count == 0)
{
LOG_FUNC_EXIT(voice->audio)
return;
}
for (i = 0; i < voice->effects.count; i += 1)
{
voice->effects.desc[i].pEffect->UnlockForProcess(voice->effects.desc[i].pEffect);
voice->effects.desc[i].pEffect->Release(voice->effects.desc[i].pEffect);
}
voice->audio->pFree(voice->effects.desc);
voice->audio->pFree(voice->effects.parameters);
voice->audio->pFree(voice->effects.parameterSizes);
voice->audio->pFree(voice->effects.parameterUpdates);
voice->audio->pFree(voice->effects.inPlaceProcessing);
LOG_FUNC_EXIT(voice->audio)
}
uint32_t FAudio_INTERNAL_VoiceOutputFrequency(
FAudioVoice *voice,
const FAudioVoiceSends *pSendList
) {
uint32_t outSampleRate;
uint32_t newResampleSamples;
uint64_t resampleSanityCheck;
LOG_FUNC_ENTER(voice->audio)
if ((pSendList == NULL) || (pSendList->SendCount == 0))
{
/* When we're deliberately given no sends, use master rate! */
outSampleRate = voice->audio->master->master.inputSampleRate;
}
else
{
outSampleRate = pSendList->pSends[0].pOutputVoice->type == FAUDIO_VOICE_MASTER ?
pSendList->pSends[0].pOutputVoice->master.inputSampleRate :
pSendList->pSends[0].pOutputVoice->mix.inputSampleRate;
}
newResampleSamples = (uint32_t) FAudio_ceil(
voice->audio->updateSize *
(double) outSampleRate /
(double) voice->audio->master->master.inputSampleRate
);
if (voice->type == FAUDIO_VOICE_SOURCE)
{
if ( (voice->src.resampleSamples != 0) &&
(newResampleSamples != voice->src.resampleSamples) &&
(voice->effects.count > 0) )
{
LOG_FUNC_EXIT(voice->audio)
return FAUDIO_E_INVALID_CALL;
}
voice->src.resampleSamples = newResampleSamples;
}
else /* (voice->type == FAUDIO_VOICE_SUBMIX) */
{
if ( (voice->mix.outputSamples != 0) &&
(newResampleSamples != voice->mix.outputSamples) &&
(voice->effects.count > 0) )
{
LOG_FUNC_EXIT(voice->audio)
return FAUDIO_E_INVALID_CALL;
}
voice->mix.outputSamples = newResampleSamples;
voice->mix.resampleStep = DOUBLE_TO_FIXED((
(double) voice->mix.inputSampleRate /
(double) outSampleRate
));
/* Because we used ceil earlier, there's a chance that
* downsampling submixes will go past the number of samples
* available. Sources can do this thanks to padding, but we
* don't have that luxury for submixes, so unfortunately we
* just have to undo the ceil and turn it into a floor.
* -flibit
*/
resampleSanityCheck = (
voice->mix.resampleStep * voice->mix.outputSamples
) >> FIXED_PRECISION;
if (resampleSanityCheck > (voice->mix.inputSamples / voice->mix.inputChannels))
{
voice->mix.outputSamples -= 1;
}
}
LOG_FUNC_EXIT(voice->audio)
return 0;
}
const float FAUDIO_INTERNAL_MATRIX_DEFAULTS[8][8][64] =
{
#include "matrix_defaults.inl"
};
/* PCM Decoding */
void FAudio_INTERNAL_DecodePCM8(
FAudioVoice *voice,
FAudioBuffer *buffer,
float *decodeCache,
uint32_t samples
) {
LOG_FUNC_ENTER(voice->audio)
FAudio_INTERNAL_Convert_U8_To_F32(
((uint8_t*) buffer->pAudioData) + (
voice->src.curBufferOffset * voice->src.format->nChannels
),
decodeCache,
samples * voice->src.format->nChannels
);
LOG_FUNC_EXIT(voice->audio)
}
void FAudio_INTERNAL_DecodePCM16(
FAudioVoice *voice,
FAudioBuffer *buffer,
float *decodeCache,
uint32_t samples
) {
LOG_FUNC_ENTER(voice->audio)
FAudio_INTERNAL_Convert_S16_To_F32(
((int16_t*) buffer->pAudioData) + (
voice->src.curBufferOffset * voice->src.format->nChannels
),
decodeCache,
samples * voice->src.format->nChannels
);
LOG_FUNC_EXIT(voice->audio)
}
void FAudio_INTERNAL_DecodePCM24(
FAudioVoice *voice,
FAudioBuffer *buffer,
float *decodeCache,
uint32_t samples
) {
uint32_t i, j;
const uint8_t *buf;
LOG_FUNC_ENTER(voice->audio)
/* FIXME: Uh... is this something that can be SIMD-ified? */
buf = buffer->pAudioData + (
voice->src.curBufferOffset * voice->src.format->nBlockAlign
);
for (i = 0; i < samples; i += 1, buf += voice->src.format->nBlockAlign)
for (j = 0; j < voice->src.format->nChannels; j += 1)
{
*decodeCache++ = ((int32_t) (
((uint32_t) buf[(j * 3) + 2] << 24) |
((uint32_t) buf[(j * 3) + 1] << 16) |
((uint32_t) buf[(j * 3) + 0] << 8)
) >> 8) / 8388607.0f;
}
LOG_FUNC_EXIT(voice->audio)
}
void FAudio_INTERNAL_DecodePCM32(
FAudioVoice *voice,
FAudioBuffer *buffer,
float *decodeCache,
uint32_t samples
) {
LOG_FUNC_ENTER(voice->audio)
FAudio_INTERNAL_Convert_S32_To_F32(
((int32_t*) buffer->pAudioData) + (
voice->src.curBufferOffset * voice->src.format->nChannels
),
decodeCache,
samples * voice->src.format->nChannels
);
LOG_FUNC_EXIT(voice->audio)
}
void FAudio_INTERNAL_DecodePCM32F(
FAudioVoice *voice,
FAudioBuffer *buffer,
float *decodeCache,
uint32_t samples
) {
LOG_FUNC_ENTER(voice->audio)
FAudio_memcpy(
decodeCache,
((float*) buffer->pAudioData) + (
voice->src.curBufferOffset * voice->src.format->nChannels
),
sizeof(float) * samples * voice->src.format->nChannels
);
LOG_FUNC_EXIT(voice->audio)
}
/* MSADPCM Decoding */
static inline int16_t FAudio_INTERNAL_ParseNibble(
uint8_t nibble,
uint8_t predictor,
int16_t *delta,
int16_t *sample1,
int16_t *sample2
) {
static const int32_t AdaptionTable[16] =
{
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
static const int32_t AdaptCoeff_1[7] =
{
256, 512, 0, 192, 240, 460, 392
};
static const int32_t AdaptCoeff_2[7] =
{
0, -256, 0, 64, 0, -208, -232
};
int8_t signedNibble;
int32_t sampleInt;
int16_t sample;
signedNibble = (int8_t) nibble;
if (signedNibble & 0x08)
{
signedNibble -= 0x10;
}
sampleInt = (
(*sample1 * AdaptCoeff_1[predictor]) +
(*sample2 * AdaptCoeff_2[predictor])
) / 256;
sampleInt += signedNibble * (*delta);
sample = FAudio_clamp(sampleInt, -32768, 32767);
*sample2 = *sample1;
*sample1 = sample;
*delta = (int16_t) (AdaptionTable[nibble] * (int32_t) (*delta) / 256);
if (*delta < 16)
{
*delta = 16;
}
return sample;
}
#define READ(item, type) \
item = *((type*) *buf); \
*buf += sizeof(type);
static inline void FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
uint8_t **buf,
int16_t *blockCache,
uint32_t align
) {
uint32_t i;
/* Temp storage for ADPCM blocks */
uint8_t predictor;
int16_t delta;
int16_t sample1;
int16_t sample2;
/* Preamble */
READ(predictor, uint8_t)
READ(delta, int16_t)
READ(sample1, int16_t)
READ(sample2, int16_t)
align -= 7;
/* Samples */
*blockCache++ = sample2;
*blockCache++ = sample1;
for (i = 0; i < align; i += 1, *buf += 1)
{
*blockCache++ = FAudio_INTERNAL_ParseNibble(
*(*buf) >> 4,
predictor,
&delta,
&sample1,
&sample2
);
*blockCache++ = FAudio_INTERNAL_ParseNibble(
*(*buf) & 0x0F,
predictor,
&delta,
&sample1,
&sample2
);
}
}
static inline void FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
uint8_t **buf,
int16_t *blockCache,
uint32_t align
) {
uint32_t i;
/* Temp storage for ADPCM blocks */
uint8_t l_predictor;
uint8_t r_predictor;
int16_t l_delta;
int16_t r_delta;
int16_t l_sample1;
int16_t r_sample1;
int16_t l_sample2;
int16_t r_sample2;
/* Preamble */
READ(l_predictor, uint8_t)
READ(r_predictor, uint8_t)
READ(l_delta, int16_t)
READ(r_delta, int16_t)
READ(l_sample1, int16_t)
READ(r_sample1, int16_t)
READ(l_sample2, int16_t)
READ(r_sample2, int16_t)
align -= 14;
/* Samples */
*blockCache++ = l_sample2;
*blockCache++ = r_sample2;
*blockCache++ = l_sample1;
*blockCache++ = r_sample1;
for (i = 0; i < align; i += 1, *buf += 1)
{
*blockCache++ = FAudio_INTERNAL_ParseNibble(
*(*buf) >> 4,
l_predictor,
&l_delta,
&l_sample1,
&l_sample2
);
*blockCache++ = FAudio_INTERNAL_ParseNibble(
*(*buf) & 0x0F,
r_predictor,
&r_delta,
&r_sample1,
&r_sample2
);
}
}
#undef READ
void FAudio_INTERNAL_DecodeMonoMSADPCM(
FAudioVoice *voice,
FAudioBuffer *buffer,
float *decodeCache,
uint32_t samples
) {
/* Loop variables */
uint32_t copy, done = 0;
/* Read pointers */
uint8_t *buf;
int32_t midOffset;
/* PCM block cache */
int16_t blockCache[1012]; /* Max block size */
/* Block size */
uint32_t bsize = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
LOG_FUNC_ENTER(voice->audio)
/* Where are we starting? */
buf = (uint8_t*) buffer->pAudioData + (
(voice->src.curBufferOffset / bsize) *
voice->src.format->nBlockAlign
);
/* Are we starting in the middle? */
midOffset = (voice->src.curBufferOffset % bsize);
/* Read in each block directly to the decode cache */
while (done < samples)
{
copy = FAudio_min(samples - done, bsize - midOffset);
FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
&buf,
blockCache,
voice->src.format->nBlockAlign
);
FAudio_INTERNAL_Convert_S16_To_F32(
blockCache + midOffset,
decodeCache,
copy
);
decodeCache += copy;
done += copy;
midOffset = 0;
}
LOG_FUNC_EXIT(voice->audio)
}
void FAudio_INTERNAL_DecodeStereoMSADPCM(
FAudioVoice *voice,
FAudioBuffer *buffer,
float *decodeCache,
uint32_t samples
) {
/* Loop variables */
uint32_t copy, done = 0;
/* Read pointers */
uint8_t *buf;
int32_t midOffset;
/* PCM block cache */
int16_t blockCache[2024]; /* Max block size */
/* Align, block size */
uint32_t bsize = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
LOG_FUNC_ENTER(voice->audio)
/* Where are we starting? */
buf = (uint8_t*) buffer->pAudioData + (
(voice->src.curBufferOffset / bsize) *
voice->src.format->nBlockAlign
);
/* Are we starting in the middle? */
midOffset = (voice->src.curBufferOffset % bsize);
/* Read in each block directly to the decode cache */
while (done < samples)
{
copy = FAudio_min(samples - done, bsize - midOffset);
FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
&buf,
blockCache,
voice->src.format->nBlockAlign
);
FAudio_INTERNAL_Convert_S16_To_F32(
blockCache + (midOffset * 2),
decodeCache,
copy * 2
);
decodeCache += copy * 2;
done += copy;
midOffset = 0;
}
LOG_FUNC_EXIT(voice->audio)
}
/* Fallback WMA decoder, get ready for spam! */
void FAudio_INTERNAL_DecodeWMAERROR(
FAudioVoice *voice,
FAudioBuffer *buffer,
float *decodeCache,
uint32_t samples
) {
LOG_FUNC_ENTER(voice->audio)
LOG_ERROR(voice->audio, "%s", "WMA IS NOT SUPPORTED IN THIS BUILD!")
FAudio_zero(decodeCache, samples * voice->src.format->nChannels * sizeof(float));
LOG_FUNC_EXIT(voice->audio)
}
/* vim: set noexpandtab shiftwidth=8 tabstop=8: */