faudio: Import upstream release 24.05.
[wine.git] / libs / faudio / src / FAudio_internal.c
bloba20e003db3ce71b3b276d803c3cdf26d084b81d1
1 /* FAudio - XAudio Reimplementation for FNA
3 * Copyright (c) 2011-2024 Ethan Lee, Luigi Auriemma, and the MonoGame Team
5 * This software is provided 'as-is', without any express or implied warranty.
6 * In no event will the authors be held liable for any damages arising from
7 * the use of this software.
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software in a
15 * product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
21 * 3. This notice may not be removed or altered from any source distribution.
23 * Ethan "flibitijibibo" Lee <flibitijibibo@flibitijibibo.com>
27 #include "FAudio_internal.h"
29 #ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION
30 void FAudio_INTERNAL_debug(
31 FAudio *audio,
32 const char *file,
33 uint32_t line,
34 const char *func,
35 const char *fmt,
36 ...
37 ) {
38 char output[1024];
39 char *out = output;
40 va_list va;
41 out[0] = '\0';
43 /* Logging extras */
44 if (audio->debug.LogThreadID)
46 out += FAudio_snprintf(
47 out,
48 sizeof(output) - (out - output),
49 "0x%" FAudio_PRIx64 " ",
50 FAudio_PlatformGetThreadID()
53 if (audio->debug.LogFileline)
55 out += FAudio_snprintf(
56 out,
57 sizeof(output) - (out - output),
58 "%s:%u ",
59 file,
60 line
63 if (audio->debug.LogFunctionName)
65 out += FAudio_snprintf(
66 out,
67 sizeof(output) - (out - output),
68 "%s ",
69 func
72 if (audio->debug.LogTiming)
74 out += FAudio_snprintf(
75 out,
76 sizeof(output) - (out - output),
77 "%dms ",
78 FAudio_timems()
82 /* The actual message... */
83 va_start(va, fmt);
84 FAudio_vsnprintf(
85 out,
86 sizeof(output) - (out - output),
87 fmt,
90 va_end(va);
92 /* Print, finally. */
93 FAudio_Log(output);
96 static const char *get_wformattag_string(const FAudioWaveFormatEx *fmt)
98 #define FMT_STRING(suffix) \
99 if (fmt->wFormatTag == FAUDIO_FORMAT_##suffix) \
101 return #suffix; \
103 FMT_STRING(PCM)
104 FMT_STRING(MSADPCM)
105 FMT_STRING(IEEE_FLOAT)
106 FMT_STRING(XMAUDIO2)
107 FMT_STRING(WMAUDIO2)
108 FMT_STRING(WMAUDIO3)
109 FMT_STRING(EXTENSIBLE)
110 #undef FMT_STRING
111 return "UNKNOWN!";
114 static const char *get_subformat_string(const FAudioWaveFormatEx *fmt)
116 const FAudioWaveFormatExtensible *fmtex = (const FAudioWaveFormatExtensible*) fmt;
118 if (fmt->wFormatTag != FAUDIO_FORMAT_EXTENSIBLE)
120 return "N/A";
122 if (!FAudio_memcmp(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID)))
124 return "IEEE_FLOAT";
126 if (!FAudio_memcmp(&fmtex->SubFormat, &DATAFORMAT_SUBTYPE_PCM, sizeof(FAudioGUID)))
128 return "PCM";
130 return "UNKNOWN!";
133 void FAudio_INTERNAL_debug_fmt(
134 FAudio *audio,
135 const char *file,
136 uint32_t line,
137 const char *func,
138 const FAudioWaveFormatEx *fmt
140 FAudio_INTERNAL_debug(
141 audio,
142 file,
143 line,
144 func,
147 "wFormatTag: 0x%x %s, "
148 "nChannels: %u, "
149 "nSamplesPerSec: %u, "
150 "wBitsPerSample: %u, "
151 "nBlockAlign: %u, "
152 "SubFormat: %s"
155 fmt->wFormatTag,
156 get_wformattag_string(fmt),
157 fmt->nChannels,
158 fmt->nSamplesPerSec,
159 fmt->wBitsPerSample,
160 fmt->nBlockAlign,
161 get_subformat_string(fmt)
164 #endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */
166 void LinkedList_AddEntry(
167 LinkedList **start,
168 void* toAdd,
169 FAudioMutex lock,
170 FAudioMallocFunc pMalloc
172 LinkedList *newEntry, *latest;
173 newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
174 newEntry->entry = toAdd;
175 newEntry->next = NULL;
176 FAudio_PlatformLockMutex(lock);
177 if (*start == NULL)
179 *start = newEntry;
181 else
183 latest = *start;
184 while (latest->next != NULL)
186 latest = latest->next;
188 latest->next = newEntry;
190 FAudio_PlatformUnlockMutex(lock);
193 void LinkedList_PrependEntry(
194 LinkedList **start,
195 void* toAdd,
196 FAudioMutex lock,
197 FAudioMallocFunc pMalloc
199 LinkedList *newEntry;
200 newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
201 newEntry->entry = toAdd;
202 FAudio_PlatformLockMutex(lock);
203 newEntry->next = *start;
204 *start = newEntry;
205 FAudio_PlatformUnlockMutex(lock);
208 void LinkedList_RemoveEntry(
209 LinkedList **start,
210 void* toRemove,
211 FAudioMutex lock,
212 FAudioFreeFunc pFree
214 LinkedList *latest, *prev;
215 FAudio_PlatformLockMutex(lock);
216 latest = *start;
217 prev = latest;
218 while (latest != NULL)
220 if (latest->entry == toRemove)
222 if (latest == prev) /* First in list */
224 *start = latest->next;
226 else
228 prev->next = latest->next;
230 pFree(latest);
231 FAudio_PlatformUnlockMutex(lock);
232 return;
234 prev = latest;
235 latest = latest->next;
237 FAudio_PlatformUnlockMutex(lock);
238 FAudio_assert(0 && "LinkedList element not found!");
241 void FAudio_INTERNAL_InsertSubmixSorted(
242 LinkedList **start,
243 FAudioSubmixVoice *toAdd,
244 FAudioMutex lock,
245 FAudioMallocFunc pMalloc
247 LinkedList *newEntry, *latest;
248 newEntry = (LinkedList*) pMalloc(sizeof(LinkedList));
249 newEntry->entry = toAdd;
250 newEntry->next = NULL;
251 FAudio_PlatformLockMutex(lock);
252 if (*start == NULL)
254 *start = newEntry;
256 else
258 latest = *start;
260 /* Special case if the new stage is lower than everyone else */
261 if (toAdd->mix.processingStage < ((FAudioSubmixVoice*) latest->entry)->mix.processingStage)
263 newEntry->next = latest;
264 *start = newEntry;
266 else
268 /* If we got here, we know that the new stage is
269 * _at least_ as high as the first submix in the list.
271 * Each loop iteration checks to see if the new stage
272 * is smaller than `latest->next`, meaning it fits
273 * between `latest` and `latest->next`.
275 while (latest->next != NULL)
277 if (toAdd->mix.processingStage < ((FAudioSubmixVoice *) latest->next->entry)->mix.processingStage)
279 newEntry->next = latest->next;
280 latest->next = newEntry;
281 break;
283 latest = latest->next;
285 /* If newEntry didn't get a `next` value, that means
286 * it didn't fall in between any stages and `latest`
287 * is the last entry in the list. Add it to the end!
289 if (newEntry->next == NULL)
291 latest->next = newEntry;
295 FAudio_PlatformUnlockMutex(lock);
298 static uint32_t FAudio_INTERNAL_GetBytesRequested(
299 FAudioSourceVoice *voice,
300 uint32_t decoding
302 uint32_t end, result;
303 FAudioBuffer *buffer;
304 FAudioWaveFormatExtensible *fmt;
305 FAudioBufferEntry *list = voice->src.bufferList;
307 LOG_FUNC_ENTER(voice->audio)
309 #ifdef HAVE_WMADEC
310 if (voice->src.wmadec != NULL)
312 /* Always 0, per the spec */
313 LOG_FUNC_EXIT(voice->audio)
314 return 0;
316 #endif /* HAVE_WMADEC */
317 while (list != NULL && decoding > 0)
319 buffer = &list->buffer;
320 if (buffer->LoopCount > 0)
322 end = (
323 /* Current loop... */
324 ((buffer->LoopBegin + buffer->LoopLength) - voice->src.curBufferOffset) +
325 /* Remaining loops... */
326 (buffer->LoopLength * buffer->LoopCount - 1) +
327 /* ... Final iteration */
328 buffer->PlayLength
331 else
333 end = (buffer->PlayBegin + buffer->PlayLength) - voice->src.curBufferOffset;
335 if (end > decoding)
337 decoding = 0;
338 break;
340 decoding -= end;
341 list = list->next;
344 /* Convert samples to bytes, factoring block alignment */
345 if (voice->src.format->wFormatTag == FAUDIO_FORMAT_MSADPCM)
347 fmt = (FAudioWaveFormatExtensible*) voice->src.format;
348 result = (
349 (decoding / fmt->Samples.wSamplesPerBlock) +
350 ((decoding % fmt->Samples.wSamplesPerBlock) > 0)
351 ) * voice->src.format->nBlockAlign;
353 else
355 result = decoding * voice->src.format->nBlockAlign;
358 LOG_FUNC_EXIT(voice->audio)
359 return result;
362 static void FAudio_INTERNAL_DecodeBuffers(
363 FAudioSourceVoice *voice,
364 uint64_t *toDecode
366 uint32_t end, endRead, decoding, decoded = 0;
367 FAudioBuffer *buffer = &voice->src.bufferList->buffer;
368 FAudioBufferEntry *toDelete;
370 LOG_FUNC_ENTER(voice->audio)
372 /* This should never go past the max ratio size */
373 FAudio_assert(*toDecode <= voice->src.decodeSamples);
375 while (decoded < *toDecode && buffer != NULL)
377 decoding = (uint32_t) *toDecode - decoded;
379 /* Start-of-buffer behavior */
380 if (voice->src.newBuffer)
382 voice->src.newBuffer = 0;
383 if ( voice->src.callback != NULL &&
384 voice->src.callback->OnBufferStart != NULL )
386 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
387 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
389 voice->src.callback->OnBufferStart(
390 voice->src.callback,
391 buffer->pContext
394 FAudio_PlatformLockMutex(voice->audio->sourceLock);
395 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
399 /* Check for end-of-buffer */
400 end = (buffer->LoopCount > 0) ?
401 (buffer->LoopBegin + buffer->LoopLength) :
402 buffer->PlayBegin + buffer->PlayLength;
403 endRead = FAudio_min(
404 end - voice->src.curBufferOffset,
405 decoding
408 /* Decode... */
409 voice->src.decode(
410 voice,
411 buffer,
412 voice->audio->decodeCache + (
413 decoded * voice->src.format->nChannels
415 endRead
418 LOG_INFO(
419 voice->audio,
420 "Voice %p, buffer %p, decoded %u samples from [%u,%u)",
421 (void*) voice,
422 (void*) buffer,
423 endRead,
424 voice->src.curBufferOffset,
425 voice->src.curBufferOffset + endRead
428 decoded += endRead;
429 voice->src.curBufferOffset += endRead;
430 voice->src.totalSamples += endRead;
432 /* End-of-buffer behavior */
433 if (endRead < decoding)
435 if (buffer->LoopCount > 0)
437 voice->src.curBufferOffset = buffer->LoopBegin;
438 if (buffer->LoopCount < FAUDIO_LOOP_INFINITE)
440 buffer->LoopCount -= 1;
442 if ( voice->src.callback != NULL &&
443 voice->src.callback->OnLoopEnd != NULL )
445 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
446 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
448 voice->src.callback->OnLoopEnd(
449 voice->src.callback,
450 buffer->pContext
453 FAudio_PlatformLockMutex(voice->audio->sourceLock);
454 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
457 else
459 #ifdef HAVE_WMADEC
460 if (voice->src.wmadec != NULL)
462 FAudio_WMADEC_end_buffer(voice);
464 #endif /* HAVE_WMADEC */
465 /* For EOS we can stop storing fraction offsets */
466 if (buffer->Flags & FAUDIO_END_OF_STREAM)
468 voice->src.curBufferOffsetDec = 0;
469 voice->src.totalSamples = 0;
472 LOG_INFO(
473 voice->audio,
474 "Voice %p, finished with buffer %p",
475 (void*) voice,
476 (void*) buffer
479 /* Change active buffer, delete finished buffer */
480 toDelete = voice->src.bufferList;
481 voice->src.bufferList = voice->src.bufferList->next;
482 if (voice->src.bufferList != NULL)
484 buffer = &voice->src.bufferList->buffer;
485 voice->src.curBufferOffset = buffer->PlayBegin;
487 else
489 buffer = NULL;
491 /* FIXME: I keep going past the buffer so fuck it */
492 FAudio_zero(
493 voice->audio->decodeCache + (
494 decoded *
495 voice->src.format->nChannels
497 sizeof(float) * (
498 (*toDecode - decoded) *
499 voice->src.format->nChannels
504 /* Callbacks */
505 if (voice->src.callback != NULL)
507 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
508 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
510 if (voice->src.callback->OnBufferEnd != NULL)
512 voice->src.callback->OnBufferEnd(
513 voice->src.callback,
514 toDelete->buffer.pContext
517 if ( toDelete->buffer.Flags & FAUDIO_END_OF_STREAM &&
518 voice->src.callback->OnStreamEnd != NULL )
520 voice->src.callback->OnStreamEnd(
521 voice->src.callback
525 /* One last chance at redemption */
526 if (buffer == NULL && voice->src.bufferList != NULL)
528 buffer = &voice->src.bufferList->buffer;
529 voice->src.curBufferOffset = buffer->PlayBegin;
532 if (buffer != NULL && voice->src.callback->OnBufferStart != NULL)
534 voice->src.callback->OnBufferStart(
535 voice->src.callback,
536 buffer->pContext
540 FAudio_PlatformLockMutex(voice->audio->sourceLock);
541 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
544 voice->audio->pFree(toDelete);
549 /* ... FIXME: I keep going past the buffer so fuck it */
550 if (buffer)
552 end = (buffer->LoopCount > 0) ?
553 (buffer->LoopBegin + buffer->LoopLength) :
554 buffer->PlayBegin + buffer->PlayLength;
555 endRead = FAudio_min(
556 end - voice->src.curBufferOffset,
557 EXTRA_DECODE_PADDING
560 voice->src.decode(
561 voice,
562 buffer,
563 voice->audio->decodeCache + (
564 decoded * voice->src.format->nChannels
566 endRead
568 /* Do NOT increment curBufferOffset! */
570 if (endRead < EXTRA_DECODE_PADDING)
572 FAudio_zero(
573 voice->audio->decodeCache + (
574 decoded * voice->src.format->nChannels
576 sizeof(float) * (
577 (EXTRA_DECODE_PADDING - endRead) *
578 voice->src.format->nChannels
583 else
585 FAudio_zero(
586 voice->audio->decodeCache + (
587 decoded * voice->src.format->nChannels
589 sizeof(float) * (
590 EXTRA_DECODE_PADDING *
591 voice->src.format->nChannels
596 *toDecode = decoded;
597 LOG_FUNC_EXIT(voice->audio)
600 static inline void FAudio_INTERNAL_FilterVoice(
601 FAudio *audio,
602 const FAudioFilterParametersEXT *filter,
603 FAudioFilterState *filterState,
604 float *samples,
605 uint32_t numSamples,
606 uint16_t numChannels
608 uint32_t j, ci;
610 LOG_FUNC_ENTER(audio)
612 /* Apply a digital state-variable filter to the voice.
613 * The difference equations of the filter are:
615 * Yl(n) = F Yb(n - 1) + Yl(n - 1)
616 * Yh(n) = x(n) - Yl(n) - OneOverQ Yb(n - 1)
617 * Yb(n) = F Yh(n) + Yb(n - 1)
618 * Yn(n) = Yl(n) + Yh(n)
620 * Please note that FAudioFilterParameters.Frequency is defined as:
622 * (2 * sin(pi * (desired filter cutoff frequency) / sampleRate))
624 * - @JohanSmet
627 for (j = 0; j < numSamples; j += 1)
628 for (ci = 0; ci < numChannels; ci += 1)
630 filterState[ci][FAudioLowPassFilter] = filterState[ci][FAudioLowPassFilter] + (filter->Frequency * filterState[ci][FAudioBandPassFilter]);
631 filterState[ci][FAudioHighPassFilter] = samples[j * numChannels + ci] - filterState[ci][FAudioLowPassFilter] - (filter->OneOverQ * filterState[ci][FAudioBandPassFilter]);
632 filterState[ci][FAudioBandPassFilter] = (filter->Frequency * filterState[ci][FAudioHighPassFilter]) + filterState[ci][FAudioBandPassFilter];
633 filterState[ci][FAudioNotchFilter] = filterState[ci][FAudioHighPassFilter] + filterState[ci][FAudioLowPassFilter];
634 samples[j * numChannels + ci] = filterState[ci][filter->Type] * filter->WetDryMix + samples[j * numChannels + ci] * (1.0 - filter->WetDryMix);
637 LOG_FUNC_EXIT(audio)
640 static void FAudio_INTERNAL_ResizeEffectChainCache(FAudio *audio, uint32_t samples)
642 LOG_FUNC_ENTER(audio)
643 if (samples > audio->effectChainSamples)
645 audio->effectChainSamples = samples;
646 audio->effectChainCache = (float*) audio->pRealloc(
647 audio->effectChainCache,
648 sizeof(float) * audio->effectChainSamples
651 LOG_FUNC_EXIT(audio)
654 static inline float *FAudio_INTERNAL_ProcessEffectChain(
655 FAudioVoice *voice,
656 float *buffer,
657 uint32_t *samples
659 uint32_t i;
660 FAPO *fapo;
661 FAPOProcessBufferParameters srcParams, dstParams;
663 LOG_FUNC_ENTER(voice->audio)
665 /* Set up the buffer to be written into */
666 srcParams.pBuffer = buffer;
667 srcParams.BufferFlags = FAPO_BUFFER_SILENT;
668 srcParams.ValidFrameCount = *samples;
669 for (i = 0; i < srcParams.ValidFrameCount; i += 1)
671 if (buffer[i] != 0.0f) /* Arbitrary! */
673 srcParams.BufferFlags = FAPO_BUFFER_VALID;
674 break;
678 /* Initialize output parameters to something sane */
679 dstParams.pBuffer = srcParams.pBuffer;
680 dstParams.BufferFlags = FAPO_BUFFER_VALID;
681 dstParams.ValidFrameCount = srcParams.ValidFrameCount;
683 /* Update parameters, process! */
684 for (i = 0; i < voice->effects.count; i += 1)
686 fapo = voice->effects.desc[i].pEffect;
688 if (!voice->effects.inPlaceProcessing[i])
690 if (dstParams.pBuffer == buffer)
692 FAudio_INTERNAL_ResizeEffectChainCache(
693 voice->audio,
694 voice->effects.desc[i].OutputChannels * srcParams.ValidFrameCount
696 dstParams.pBuffer = voice->audio->effectChainCache;
698 else
700 /* FIXME: What if this is smaller because
701 * inputChannels < desc[i].OutputChannels?
703 dstParams.pBuffer = buffer;
706 FAudio_zero(
707 dstParams.pBuffer,
708 voice->effects.desc[i].OutputChannels * srcParams.ValidFrameCount * sizeof(float)
712 if (voice->effects.parameterUpdates[i])
714 fapo->SetParameters(
715 fapo,
716 voice->effects.parameters[i],
717 voice->effects.parameterSizes[i]
719 voice->effects.parameterUpdates[i] = 0;
722 fapo->Process(
723 fapo,
725 &srcParams,
727 &dstParams,
728 voice->effects.desc[i].InitialState
731 FAudio_memcpy(&srcParams, &dstParams, sizeof(dstParams));
734 *samples = dstParams.ValidFrameCount;
736 /* Save the output buffer-flags so the mixer-function can determine when it's save to stop processing the effect chain */
737 voice->effects.state = dstParams.BufferFlags;
739 LOG_FUNC_EXIT(voice->audio)
740 return (float*) dstParams.pBuffer;
743 static void FAudio_INTERNAL_ResizeResampleCache(FAudio *audio, uint32_t samples)
745 LOG_FUNC_ENTER(audio)
746 if (samples > audio->resampleSamples)
748 audio->resampleSamples = samples;
749 audio->resampleCache = (float*) audio->pRealloc(
750 audio->resampleCache,
751 sizeof(float) * audio->resampleSamples
754 LOG_FUNC_EXIT(audio)
757 static void FAudio_INTERNAL_MixSource(FAudioSourceVoice *voice)
759 /* Iterators */
760 uint32_t i;
761 /* Decode/Resample variables */
762 uint64_t toDecode;
763 uint64_t toResample;
764 /* Output mix variables */
765 float *stream;
766 uint32_t mixed;
767 uint32_t oChan;
768 FAudioVoice *out;
769 uint32_t outputRate;
770 double stepd;
771 float *finalSamples;
773 LOG_FUNC_ENTER(voice->audio)
775 FAudio_PlatformLockMutex(voice->sendLock);
776 LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
778 /* Calculate the resample stepping value */
779 if (voice->src.resampleFreq != voice->src.freqRatio * voice->src.format->nSamplesPerSec)
781 out = (voice->sends.SendCount == 0) ?
782 voice->audio->master : /* Barf */
783 voice->sends.pSends->pOutputVoice;
784 outputRate = (out->type == FAUDIO_VOICE_MASTER) ?
785 out->master.inputSampleRate :
786 out->mix.inputSampleRate;
787 stepd = (
788 voice->src.freqRatio *
789 (double) voice->src.format->nSamplesPerSec /
790 (double) outputRate
792 voice->src.resampleStep = DOUBLE_TO_FIXED(stepd);
793 voice->src.resampleFreq = voice->src.freqRatio * voice->src.format->nSamplesPerSec;
796 if (voice->src.active == 2)
798 /* We're just playing tails, skip all buffer stuff */
799 FAudio_INTERNAL_ResizeResampleCache(
800 voice->audio,
801 voice->src.resampleSamples * voice->src.format->nChannels
803 mixed = voice->src.resampleSamples;
804 FAudio_zero(
805 voice->audio->resampleCache,
806 mixed * voice->src.format->nChannels * sizeof(float)
808 finalSamples = voice->audio->resampleCache;
809 goto sendwork;
812 /* Base decode size, int to fixed... */
813 toDecode = voice->src.resampleSamples * voice->src.resampleStep;
814 /* ... rounded up based on current offset... */
815 toDecode += voice->src.curBufferOffsetDec + FIXED_FRACTION_MASK;
816 /* ... fixed to int, truncating extra fraction from rounding. */
817 toDecode >>= FIXED_PRECISION;
819 /* First voice callback */
820 if ( voice->src.callback != NULL &&
821 voice->src.callback->OnVoiceProcessingPassStart != NULL )
823 FAudio_PlatformUnlockMutex(voice->sendLock);
824 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
826 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
827 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
829 voice->src.callback->OnVoiceProcessingPassStart(
830 voice->src.callback,
831 FAudio_INTERNAL_GetBytesRequested(voice, (uint32_t) toDecode)
834 FAudio_PlatformLockMutex(voice->audio->sourceLock);
835 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
837 FAudio_PlatformLockMutex(voice->sendLock);
838 LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
841 FAudio_PlatformLockMutex(voice->src.bufferLock);
842 LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
844 /* Nothing to do? */
845 if (voice->src.bufferList == NULL)
847 FAudio_PlatformUnlockMutex(voice->src.bufferLock);
848 LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
850 if (voice->effects.count > 0 && voice->effects.state != FAPO_BUFFER_SILENT)
852 /* do not stop while the effect chain generates a non-silent buffer */
853 FAudio_INTERNAL_ResizeResampleCache(
854 voice->audio,
855 voice->src.resampleSamples * voice->src.format->nChannels
857 mixed = voice->src.resampleSamples;
858 FAudio_zero(
859 voice->audio->resampleCache,
860 mixed * voice->src.format->nChannels * sizeof(float)
862 finalSamples = voice->audio->resampleCache;
863 goto sendwork;
866 FAudio_PlatformUnlockMutex(voice->sendLock);
867 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
869 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
870 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
872 if ( voice->src.callback != NULL &&
873 voice->src.callback->OnVoiceProcessingPassEnd != NULL)
875 voice->src.callback->OnVoiceProcessingPassEnd(
876 voice->src.callback
880 FAudio_PlatformLockMutex(voice->audio->sourceLock);
881 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
883 LOG_FUNC_EXIT(voice->audio)
884 return;
887 /* Decode... */
888 FAudio_INTERNAL_DecodeBuffers(voice, &toDecode);
890 /* Subtract any padding samples from the total, if applicable */
891 if ( voice->src.curBufferOffsetDec > 0 &&
892 voice->src.totalSamples > 0 )
894 voice->src.totalSamples -= 1;
897 /* Okay, we're done messing with client data */
898 if ( voice->src.callback != NULL &&
899 voice->src.callback->OnVoiceProcessingPassEnd != NULL)
901 FAudio_PlatformUnlockMutex(voice->src.bufferLock);
902 LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
904 FAudio_PlatformUnlockMutex(voice->sendLock);
905 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
907 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
908 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
910 voice->src.callback->OnVoiceProcessingPassEnd(
911 voice->src.callback
914 FAudio_PlatformLockMutex(voice->audio->sourceLock);
915 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
917 FAudio_PlatformLockMutex(voice->sendLock);
918 LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
920 FAudio_PlatformLockMutex(voice->src.bufferLock);
921 LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
924 /* Nothing to resample? */
925 if (toDecode == 0)
927 FAudio_PlatformUnlockMutex(voice->src.bufferLock);
928 LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
930 FAudio_PlatformUnlockMutex(voice->sendLock);
931 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
933 LOG_FUNC_EXIT(voice->audio)
934 return;
937 /* int to fixed... */
938 toResample = toDecode << FIXED_PRECISION;
939 /* ... round back down based on current offset... */
940 toResample -= voice->src.curBufferOffsetDec;
941 /* ... but also ceil for any fraction value... */
942 toResample += FIXED_FRACTION_MASK;
943 /* ... undo step size, fixed to int. */
944 toResample /= voice->src.resampleStep;
945 /* Add the padding, for some reason this helps? */
946 toResample += EXTRA_DECODE_PADDING;
947 /* FIXME: I feel like this should be an assert but I suck */
948 toResample = FAudio_min(toResample, voice->src.resampleSamples);
950 /* Resample... */
951 if (voice->src.resampleStep == FIXED_ONE)
953 /* Actually, just use the existing buffer... */
954 finalSamples = voice->audio->decodeCache;
956 else
958 FAudio_INTERNAL_ResizeResampleCache(
959 voice->audio,
960 voice->src.resampleSamples * voice->src.format->nChannels
962 voice->src.resample(
963 voice->audio->decodeCache,
964 voice->audio->resampleCache,
965 &voice->src.resampleOffset,
966 voice->src.resampleStep,
967 toResample,
968 (uint8_t) voice->src.format->nChannels
970 finalSamples = voice->audio->resampleCache;
973 /* Update buffer offsets */
974 if (voice->src.bufferList != NULL)
976 /* Increment fixed offset by resample size, int to fixed... */
977 voice->src.curBufferOffsetDec += toResample * voice->src.resampleStep;
978 /* ... chop off any ints we got from the above increment */
979 voice->src.curBufferOffsetDec &= FIXED_FRACTION_MASK;
981 /* Dec >0? We need one frame from the past...
982 * FIXME: We can't go back to a prev buffer though?
984 if ( voice->src.curBufferOffsetDec > 0 &&
985 voice->src.curBufferOffset > 0 )
987 voice->src.curBufferOffset -= 1;
990 else
992 voice->src.curBufferOffsetDec = 0;
993 voice->src.curBufferOffset = 0;
996 /* Done with buffers, finally. */
997 FAudio_PlatformUnlockMutex(voice->src.bufferLock);
998 LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
999 mixed = (uint32_t) toResample;
1001 sendwork:
1003 /* Filters */
1004 if (voice->flags & FAUDIO_VOICE_USEFILTER)
1006 FAudio_PlatformLockMutex(voice->filterLock);
1007 LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
1008 FAudio_INTERNAL_FilterVoice(
1009 voice->audio,
1010 &voice->filter,
1011 voice->filterState,
1012 finalSamples,
1013 mixed,
1014 voice->src.format->nChannels
1016 FAudio_PlatformUnlockMutex(voice->filterLock);
1017 LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
1020 /* Process effect chain */
1021 FAudio_PlatformLockMutex(voice->effectLock);
1022 LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1023 if (voice->effects.count > 0)
1025 /* If we didn't get the full size of the update, we have to fill
1026 * it with silence so the effect can process a whole update
1028 if (mixed < voice->src.resampleSamples)
1030 FAudio_zero(
1031 finalSamples + (mixed * voice->src.format->nChannels),
1032 (voice->src.resampleSamples - mixed) * voice->src.format->nChannels * sizeof(float)
1034 mixed = voice->src.resampleSamples;
1036 finalSamples = FAudio_INTERNAL_ProcessEffectChain(
1037 voice,
1038 finalSamples,
1039 &mixed
1042 FAudio_PlatformUnlockMutex(voice->effectLock);
1043 LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1045 /* Nowhere to send it? Just skip the rest...*/
1046 if (voice->sends.SendCount == 0)
1048 FAudio_PlatformUnlockMutex(voice->sendLock);
1049 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1050 LOG_FUNC_EXIT(voice->audio)
1051 return;
1054 /* Send float cache to sends */
1055 FAudio_PlatformLockMutex(voice->volumeLock);
1056 LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
1057 for (i = 0; i < voice->sends.SendCount; i += 1)
1059 out = voice->sends.pSends[i].pOutputVoice;
1060 if (out->type == FAUDIO_VOICE_MASTER)
1062 stream = out->master.output;
1063 oChan = out->master.inputChannels;
1065 else
1067 stream = out->mix.inputCache;
1068 oChan = out->mix.inputChannels;
1071 voice->sendMix[i](
1072 mixed,
1073 voice->outputChannels,
1074 oChan,
1075 finalSamples,
1076 stream,
1077 voice->mixCoefficients[i]
1080 if (voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER)
1082 FAudio_INTERNAL_FilterVoice(
1083 voice->audio,
1084 &voice->sendFilter[i],
1085 voice->sendFilterState[i],
1086 stream,
1087 mixed,
1088 oChan
1092 FAudio_PlatformUnlockMutex(voice->volumeLock);
1093 LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
1095 FAudio_PlatformUnlockMutex(voice->sendLock);
1096 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1097 LOG_FUNC_EXIT(voice->audio)
1100 static void FAudio_INTERNAL_MixSubmix(FAudioSubmixVoice *voice)
1102 uint32_t i;
1103 float *stream;
1104 uint32_t oChan;
1105 FAudioVoice *out;
1106 uint32_t resampled;
1107 uint64_t resampleOffset = 0;
1108 float *finalSamples;
1110 LOG_FUNC_ENTER(voice->audio)
1111 FAudio_PlatformLockMutex(voice->sendLock);
1112 LOG_MUTEX_LOCK(voice->audio, voice->sendLock)
1114 /* Resample */
1115 if (voice->mix.resampleStep == FIXED_ONE)
1117 /* Actually, just use the existing buffer... */
1118 finalSamples = voice->mix.inputCache;
1120 else
1122 FAudio_INTERNAL_ResizeResampleCache(
1123 voice->audio,
1124 voice->mix.outputSamples * voice->mix.inputChannels
1126 voice->mix.resample(
1127 voice->mix.inputCache,
1128 voice->audio->resampleCache,
1129 &resampleOffset,
1130 voice->mix.resampleStep,
1131 voice->mix.outputSamples,
1132 (uint8_t) voice->mix.inputChannels
1134 finalSamples = voice->audio->resampleCache;
1136 resampled = voice->mix.outputSamples * voice->mix.inputChannels;
1138 /* Submix overall volume is applied _before_ effects/filters, blech! */
1139 if (voice->volume != 1.0f)
1141 FAudio_INTERNAL_Amplify(
1142 finalSamples,
1143 resampled,
1144 voice->volume
1147 resampled /= voice->mix.inputChannels;
1149 /* Filters */
1150 if (voice->flags & FAUDIO_VOICE_USEFILTER)
1152 FAudio_PlatformLockMutex(voice->filterLock);
1153 LOG_MUTEX_LOCK(voice->audio, voice->filterLock)
1154 FAudio_INTERNAL_FilterVoice(
1155 voice->audio,
1156 &voice->filter,
1157 voice->filterState,
1158 finalSamples,
1159 resampled,
1160 voice->mix.inputChannels
1162 FAudio_PlatformUnlockMutex(voice->filterLock);
1163 LOG_MUTEX_UNLOCK(voice->audio, voice->filterLock)
1166 /* Process effect chain */
1167 FAudio_PlatformLockMutex(voice->effectLock);
1168 LOG_MUTEX_LOCK(voice->audio, voice->effectLock)
1169 if (voice->effects.count > 0)
1171 finalSamples = FAudio_INTERNAL_ProcessEffectChain(
1172 voice,
1173 finalSamples,
1174 &resampled
1177 FAudio_PlatformUnlockMutex(voice->effectLock);
1178 LOG_MUTEX_UNLOCK(voice->audio, voice->effectLock)
1180 /* Nothing more to do? */
1181 if (voice->sends.SendCount == 0)
1183 goto end;
1186 /* Send float cache to sends */
1187 FAudio_PlatformLockMutex(voice->volumeLock);
1188 LOG_MUTEX_LOCK(voice->audio, voice->volumeLock)
1189 for (i = 0; i < voice->sends.SendCount; i += 1)
1191 out = voice->sends.pSends[i].pOutputVoice;
1192 if (out->type == FAUDIO_VOICE_MASTER)
1194 stream = out->master.output;
1195 oChan = out->master.inputChannels;
1197 else
1199 stream = out->mix.inputCache;
1200 oChan = out->mix.inputChannels;
1203 voice->sendMix[i](
1204 resampled,
1205 voice->outputChannels,
1206 oChan,
1207 finalSamples,
1208 stream,
1209 voice->mixCoefficients[i]
1212 if (voice->sends.pSends[i].Flags & FAUDIO_SEND_USEFILTER)
1214 FAudio_INTERNAL_FilterVoice(
1215 voice->audio,
1216 &voice->sendFilter[i],
1217 voice->sendFilterState[i],
1218 stream,
1219 resampled,
1220 oChan
1224 FAudio_PlatformUnlockMutex(voice->volumeLock);
1225 LOG_MUTEX_UNLOCK(voice->audio, voice->volumeLock)
1227 /* Zero this at the end, for the next update */
1228 end:
1229 FAudio_PlatformUnlockMutex(voice->sendLock);
1230 LOG_MUTEX_UNLOCK(voice->audio, voice->sendLock)
1231 FAudio_zero(
1232 voice->mix.inputCache,
1233 sizeof(float) * voice->mix.inputSamples
1235 LOG_FUNC_EXIT(voice->audio)
1238 static void FAudio_INTERNAL_FlushPendingBuffers(FAudioSourceVoice *voice)
1240 FAudioBufferEntry *entry;
1242 FAudio_PlatformLockMutex(voice->src.bufferLock);
1243 LOG_MUTEX_LOCK(voice->audio, voice->src.bufferLock)
1245 /* Remove pending flushed buffers and send an event for each one */
1246 while (voice->src.flushList != NULL)
1248 entry = voice->src.flushList;
1249 voice->src.flushList = voice->src.flushList->next;
1251 if (voice->src.callback != NULL && voice->src.callback->OnBufferEnd != NULL)
1253 FAudio_PlatformUnlockMutex(voice->audio->sourceLock);
1254 LOG_MUTEX_UNLOCK(voice->audio, voice->audio->sourceLock)
1256 voice->src.callback->OnBufferEnd(
1257 voice->src.callback,
1258 entry->buffer.pContext
1261 FAudio_PlatformLockMutex(voice->audio->sourceLock);
1262 LOG_MUTEX_LOCK(voice->audio, voice->audio->sourceLock)
1264 voice->audio->pFree(entry);
1267 FAudio_PlatformUnlockMutex(voice->src.bufferLock);
1268 LOG_MUTEX_UNLOCK(voice->audio, voice->src.bufferLock)
1271 static void FAUDIOCALL FAudio_INTERNAL_GenerateOutput(FAudio *audio, float *output)
1273 uint32_t totalSamples;
1274 LinkedList *list;
1275 float *effectOut;
1276 FAudioEngineCallback *callback;
1278 LOG_FUNC_ENTER(audio)
1279 if (!audio->active)
1281 LOG_FUNC_EXIT(audio)
1282 return;
1285 /* Apply any committed changes */
1286 FAudio_OPERATIONSET_Execute(audio);
1288 /* ProcessingPassStart callbacks */
1289 FAudio_PlatformLockMutex(audio->callbackLock);
1290 LOG_MUTEX_LOCK(audio, audio->callbackLock)
1291 list = audio->callbacks;
1292 while (list != NULL)
1294 callback = (FAudioEngineCallback*) list->entry;
1295 if (callback->OnProcessingPassStart != NULL)
1297 callback->OnProcessingPassStart(
1298 callback
1301 list = list->next;
1303 FAudio_PlatformUnlockMutex(audio->callbackLock);
1304 LOG_MUTEX_UNLOCK(audio, audio->callbackLock)
1306 /* Writes to master will directly write to output, but ONLY if there
1307 * isn't any channel-changing effect processing to do first.
1309 if (audio->master->master.effectCache != NULL)
1311 audio->master->master.output = audio->master->master.effectCache;
1312 FAudio_zero(
1313 audio->master->master.effectCache,
1315 sizeof(float) *
1316 audio->updateSize *
1317 audio->master->master.inputChannels
1321 else
1323 audio->master->master.output = output;
1326 /* Mix sources */
1327 FAudio_PlatformLockMutex(audio->sourceLock);
1328 LOG_MUTEX_LOCK(audio, audio->sourceLock)
1329 list = audio->sources;
1330 while (list != NULL)
1332 audio->processingSource = (FAudioSourceVoice*) list->entry;
1334 FAudio_INTERNAL_FlushPendingBuffers(audio->processingSource);
1335 if (audio->processingSource->src.active)
1337 FAudio_INTERNAL_MixSource(audio->processingSource);
1338 FAudio_INTERNAL_FlushPendingBuffers(audio->processingSource);
1341 list = list->next;
1343 audio->processingSource = NULL;
1344 FAudio_PlatformUnlockMutex(audio->sourceLock);
1345 LOG_MUTEX_UNLOCK(audio, audio->sourceLock)
1347 /* Mix submixes, ordered by processing stage */
1348 FAudio_PlatformLockMutex(audio->submixLock);
1349 LOG_MUTEX_LOCK(audio, audio->submixLock)
1350 list = audio->submixes;
1351 while (list != NULL)
1353 FAudio_INTERNAL_MixSubmix((FAudioSubmixVoice*) list->entry);
1354 list = list->next;
1356 FAudio_PlatformUnlockMutex(audio->submixLock);
1357 LOG_MUTEX_UNLOCK(audio, audio->submixLock)
1359 /* Apply master volume */
1360 if (audio->master->volume != 1.0f)
1362 FAudio_INTERNAL_Amplify(
1363 audio->master->master.output,
1364 audio->updateSize * audio->master->master.inputChannels,
1365 audio->master->volume
1369 /* Process master effect chain */
1370 FAudio_PlatformLockMutex(audio->master->effectLock);
1371 LOG_MUTEX_LOCK(audio, audio->master->effectLock)
1372 if (audio->master->effects.count > 0)
1374 totalSamples = audio->updateSize;
1375 effectOut = FAudio_INTERNAL_ProcessEffectChain(
1376 audio->master,
1377 audio->master->master.output,
1378 &totalSamples
1381 if (effectOut != output)
1383 FAudio_memcpy(
1384 output,
1385 effectOut,
1386 totalSamples * audio->master->outputChannels * sizeof(float)
1389 if (totalSamples < audio->updateSize)
1391 FAudio_zero(
1392 output + (totalSamples * audio->master->outputChannels),
1393 (audio->updateSize - totalSamples) * sizeof(float)
1397 FAudio_PlatformUnlockMutex(audio->master->effectLock);
1398 LOG_MUTEX_UNLOCK(audio, audio->master->effectLock)
1400 /* OnProcessingPassEnd callbacks */
1401 FAudio_PlatformLockMutex(audio->callbackLock);
1402 LOG_MUTEX_LOCK(audio, audio->callbackLock)
1403 list = audio->callbacks;
1404 while (list != NULL)
1406 callback = (FAudioEngineCallback*) list->entry;
1407 if (callback->OnProcessingPassEnd != NULL)
1409 callback->OnProcessingPassEnd(
1410 callback
1413 list = list->next;
1415 FAudio_PlatformUnlockMutex(audio->callbackLock);
1416 LOG_MUTEX_UNLOCK(audio, audio->callbackLock)
1418 LOG_FUNC_EXIT(audio)
1421 void FAudio_INTERNAL_UpdateEngine(FAudio *audio, float *output)
1423 LOG_FUNC_ENTER(audio)
1424 if (audio->pClientEngineProc)
1426 audio->pClientEngineProc(
1427 &FAudio_INTERNAL_GenerateOutput,
1428 audio,
1429 output,
1430 audio->clientEngineUser
1433 else
1435 FAudio_INTERNAL_GenerateOutput(audio, output);
1437 LOG_FUNC_EXIT(audio)
1440 void FAudio_INTERNAL_ResizeDecodeCache(FAudio *audio, uint32_t samples)
1442 LOG_FUNC_ENTER(audio)
1443 FAudio_PlatformLockMutex(audio->sourceLock);
1444 LOG_MUTEX_LOCK(audio, audio->sourceLock)
1445 if (samples > audio->decodeSamples)
1447 audio->decodeSamples = samples;
1448 audio->decodeCache = (float*) audio->pRealloc(
1449 audio->decodeCache,
1450 sizeof(float) * audio->decodeSamples
1453 FAudio_PlatformUnlockMutex(audio->sourceLock);
1454 LOG_MUTEX_UNLOCK(audio, audio->sourceLock)
1455 LOG_FUNC_EXIT(audio)
1458 void FAudio_INTERNAL_AllocEffectChain(
1459 FAudioVoice *voice,
1460 const FAudioEffectChain *pEffectChain
1462 uint32_t i;
1464 LOG_FUNC_ENTER(voice->audio)
1465 voice->effects.state = FAPO_BUFFER_VALID;
1466 voice->effects.count = pEffectChain->EffectCount;
1467 if (voice->effects.count == 0)
1469 LOG_FUNC_EXIT(voice->audio)
1470 return;
1473 for (i = 0; i < pEffectChain->EffectCount; i += 1)
1475 pEffectChain->pEffectDescriptors[i].pEffect->AddRef(pEffectChain->pEffectDescriptors[i].pEffect);
1478 voice->effects.desc = (FAudioEffectDescriptor*) voice->audio->pMalloc(
1479 voice->effects.count * sizeof(FAudioEffectDescriptor)
1481 FAudio_memcpy(
1482 voice->effects.desc,
1483 pEffectChain->pEffectDescriptors,
1484 voice->effects.count * sizeof(FAudioEffectDescriptor)
1486 #define ALLOC_EFFECT_PROPERTY(prop, type) \
1487 voice->effects.prop = (type*) voice->audio->pMalloc( \
1488 voice->effects.count * sizeof(type) \
1489 ); \
1490 FAudio_zero( \
1491 voice->effects.prop, \
1492 voice->effects.count * sizeof(type) \
1494 ALLOC_EFFECT_PROPERTY(parameters, void*)
1495 ALLOC_EFFECT_PROPERTY(parameterSizes, uint32_t)
1496 ALLOC_EFFECT_PROPERTY(parameterUpdates, uint8_t)
1497 ALLOC_EFFECT_PROPERTY(inPlaceProcessing, uint8_t)
1498 #undef ALLOC_EFFECT_PROPERTY
1499 LOG_FUNC_EXIT(voice->audio)
1502 void FAudio_INTERNAL_FreeEffectChain(FAudioVoice *voice)
1504 uint32_t i;
1506 LOG_FUNC_ENTER(voice->audio)
1507 if (voice->effects.count == 0)
1509 LOG_FUNC_EXIT(voice->audio)
1510 return;
1513 for (i = 0; i < voice->effects.count; i += 1)
1515 voice->effects.desc[i].pEffect->UnlockForProcess(voice->effects.desc[i].pEffect);
1516 voice->effects.desc[i].pEffect->Release(voice->effects.desc[i].pEffect);
1519 voice->audio->pFree(voice->effects.desc);
1520 voice->audio->pFree(voice->effects.parameters);
1521 voice->audio->pFree(voice->effects.parameterSizes);
1522 voice->audio->pFree(voice->effects.parameterUpdates);
1523 voice->audio->pFree(voice->effects.inPlaceProcessing);
1524 LOG_FUNC_EXIT(voice->audio)
1527 uint32_t FAudio_INTERNAL_VoiceOutputFrequency(
1528 FAudioVoice *voice,
1529 const FAudioVoiceSends *pSendList
1531 uint32_t outSampleRate;
1532 uint32_t newResampleSamples;
1533 uint64_t resampleSanityCheck;
1535 LOG_FUNC_ENTER(voice->audio)
1537 if ((pSendList == NULL) || (pSendList->SendCount == 0))
1539 /* When we're deliberately given no sends, use master rate! */
1540 outSampleRate = voice->audio->master->master.inputSampleRate;
1542 else
1544 outSampleRate = pSendList->pSends[0].pOutputVoice->type == FAUDIO_VOICE_MASTER ?
1545 pSendList->pSends[0].pOutputVoice->master.inputSampleRate :
1546 pSendList->pSends[0].pOutputVoice->mix.inputSampleRate;
1548 newResampleSamples = (uint32_t) FAudio_ceil(
1549 voice->audio->updateSize *
1550 (double) outSampleRate /
1551 (double) voice->audio->master->master.inputSampleRate
1553 if (voice->type == FAUDIO_VOICE_SOURCE)
1555 if ( (voice->src.resampleSamples != 0) &&
1556 (newResampleSamples != voice->src.resampleSamples) &&
1557 (voice->effects.count > 0) )
1559 LOG_FUNC_EXIT(voice->audio)
1560 return FAUDIO_E_INVALID_CALL;
1562 voice->src.resampleSamples = newResampleSamples;
1564 else /* (voice->type == FAUDIO_VOICE_SUBMIX) */
1566 if ( (voice->mix.outputSamples != 0) &&
1567 (newResampleSamples != voice->mix.outputSamples) &&
1568 (voice->effects.count > 0) )
1570 LOG_FUNC_EXIT(voice->audio)
1571 return FAUDIO_E_INVALID_CALL;
1573 voice->mix.outputSamples = newResampleSamples;
1575 voice->mix.resampleStep = DOUBLE_TO_FIXED((
1576 (double) voice->mix.inputSampleRate /
1577 (double) outSampleRate
1580 /* Because we used ceil earlier, there's a chance that
1581 * downsampling submixes will go past the number of samples
1582 * available. Sources can do this thanks to padding, but we
1583 * don't have that luxury for submixes, so unfortunately we
1584 * just have to undo the ceil and turn it into a floor.
1585 * -flibit
1587 resampleSanityCheck = (
1588 voice->mix.resampleStep * voice->mix.outputSamples
1589 ) >> FIXED_PRECISION;
1590 if (resampleSanityCheck > (voice->mix.inputSamples / voice->mix.inputChannels))
1592 voice->mix.outputSamples -= 1;
1596 LOG_FUNC_EXIT(voice->audio)
1597 return 0;
1600 const float FAUDIO_INTERNAL_MATRIX_DEFAULTS[8][8][64] =
1602 #include "matrix_defaults.inl"
1605 /* PCM Decoding */
1607 void FAudio_INTERNAL_DecodePCM8(
1608 FAudioVoice *voice,
1609 FAudioBuffer *buffer,
1610 float *decodeCache,
1611 uint32_t samples
1613 LOG_FUNC_ENTER(voice->audio)
1614 FAudio_INTERNAL_Convert_U8_To_F32(
1615 ((uint8_t*) buffer->pAudioData) + (
1616 voice->src.curBufferOffset * voice->src.format->nChannels
1618 decodeCache,
1619 samples * voice->src.format->nChannels
1621 LOG_FUNC_EXIT(voice->audio)
1624 void FAudio_INTERNAL_DecodePCM16(
1625 FAudioVoice *voice,
1626 FAudioBuffer *buffer,
1627 float *decodeCache,
1628 uint32_t samples
1630 LOG_FUNC_ENTER(voice->audio)
1631 FAudio_INTERNAL_Convert_S16_To_F32(
1632 ((int16_t*) buffer->pAudioData) + (
1633 voice->src.curBufferOffset * voice->src.format->nChannels
1635 decodeCache,
1636 samples * voice->src.format->nChannels
1638 LOG_FUNC_EXIT(voice->audio)
1641 void FAudio_INTERNAL_DecodePCM24(
1642 FAudioVoice *voice,
1643 FAudioBuffer *buffer,
1644 float *decodeCache,
1645 uint32_t samples
1647 uint32_t i, j;
1648 const uint8_t *buf;
1649 LOG_FUNC_ENTER(voice->audio)
1651 /* FIXME: Uh... is this something that can be SIMD-ified? */
1652 buf = buffer->pAudioData + (
1653 voice->src.curBufferOffset * voice->src.format->nBlockAlign
1655 for (i = 0; i < samples; i += 1, buf += voice->src.format->nBlockAlign)
1656 for (j = 0; j < voice->src.format->nChannels; j += 1)
1658 *decodeCache++ = ((int32_t) (
1659 ((uint32_t) buf[(j * 3) + 2] << 24) |
1660 ((uint32_t) buf[(j * 3) + 1] << 16) |
1661 ((uint32_t) buf[(j * 3) + 0] << 8)
1662 ) >> 8) / 8388607.0f;
1665 LOG_FUNC_EXIT(voice->audio)
1668 void FAudio_INTERNAL_DecodePCM32(
1669 FAudioVoice *voice,
1670 FAudioBuffer *buffer,
1671 float *decodeCache,
1672 uint32_t samples
1674 LOG_FUNC_ENTER(voice->audio)
1675 FAudio_INTERNAL_Convert_S32_To_F32(
1676 ((int32_t*) buffer->pAudioData) + (
1677 voice->src.curBufferOffset * voice->src.format->nChannels
1679 decodeCache,
1680 samples * voice->src.format->nChannels
1682 LOG_FUNC_EXIT(voice->audio)
1685 void FAudio_INTERNAL_DecodePCM32F(
1686 FAudioVoice *voice,
1687 FAudioBuffer *buffer,
1688 float *decodeCache,
1689 uint32_t samples
1691 LOG_FUNC_ENTER(voice->audio)
1692 FAudio_memcpy(
1693 decodeCache,
1694 ((float*) buffer->pAudioData) + (
1695 voice->src.curBufferOffset * voice->src.format->nChannels
1697 sizeof(float) * samples * voice->src.format->nChannels
1699 LOG_FUNC_EXIT(voice->audio)
1702 /* MSADPCM Decoding */
1704 static inline int16_t FAudio_INTERNAL_ParseNibble(
1705 uint8_t nibble,
1706 uint8_t predictor,
1707 int16_t *delta,
1708 int16_t *sample1,
1709 int16_t *sample2
1711 static const int32_t AdaptionTable[16] =
1713 230, 230, 230, 230, 307, 409, 512, 614,
1714 768, 614, 512, 409, 307, 230, 230, 230
1716 static const int32_t AdaptCoeff_1[7] =
1718 256, 512, 0, 192, 240, 460, 392
1720 static const int32_t AdaptCoeff_2[7] =
1722 0, -256, 0, 64, 0, -208, -232
1725 int8_t signedNibble;
1726 int32_t sampleInt;
1727 int16_t sample;
1729 signedNibble = (int8_t) nibble;
1730 if (signedNibble & 0x08)
1732 signedNibble -= 0x10;
1735 sampleInt = (
1736 (*sample1 * AdaptCoeff_1[predictor]) +
1737 (*sample2 * AdaptCoeff_2[predictor])
1738 ) / 256;
1739 sampleInt += signedNibble * (*delta);
1740 sample = FAudio_clamp(sampleInt, -32768, 32767);
1742 *sample2 = *sample1;
1743 *sample1 = sample;
1744 *delta = (int16_t) (AdaptionTable[nibble] * (int32_t) (*delta) / 256);
1745 if (*delta < 16)
1747 *delta = 16;
1749 return sample;
1752 #define READ(item, type) \
1753 item = *((type*) *buf); \
1754 *buf += sizeof(type);
1756 static inline void FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
1757 uint8_t **buf,
1758 int16_t *blockCache,
1759 uint32_t align
1761 uint32_t i;
1763 /* Temp storage for ADPCM blocks */
1764 uint8_t predictor;
1765 int16_t delta;
1766 int16_t sample1;
1767 int16_t sample2;
1769 /* Preamble */
1770 READ(predictor, uint8_t)
1771 READ(delta, int16_t)
1772 READ(sample1, int16_t)
1773 READ(sample2, int16_t)
1774 align -= 7;
1776 /* Samples */
1777 *blockCache++ = sample2;
1778 *blockCache++ = sample1;
1779 for (i = 0; i < align; i += 1, *buf += 1)
1781 *blockCache++ = FAudio_INTERNAL_ParseNibble(
1782 *(*buf) >> 4,
1783 predictor,
1784 &delta,
1785 &sample1,
1786 &sample2
1788 *blockCache++ = FAudio_INTERNAL_ParseNibble(
1789 *(*buf) & 0x0F,
1790 predictor,
1791 &delta,
1792 &sample1,
1793 &sample2
1798 static inline void FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
1799 uint8_t **buf,
1800 int16_t *blockCache,
1801 uint32_t align
1803 uint32_t i;
1805 /* Temp storage for ADPCM blocks */
1806 uint8_t l_predictor;
1807 uint8_t r_predictor;
1808 int16_t l_delta;
1809 int16_t r_delta;
1810 int16_t l_sample1;
1811 int16_t r_sample1;
1812 int16_t l_sample2;
1813 int16_t r_sample2;
1815 /* Preamble */
1816 READ(l_predictor, uint8_t)
1817 READ(r_predictor, uint8_t)
1818 READ(l_delta, int16_t)
1819 READ(r_delta, int16_t)
1820 READ(l_sample1, int16_t)
1821 READ(r_sample1, int16_t)
1822 READ(l_sample2, int16_t)
1823 READ(r_sample2, int16_t)
1824 align -= 14;
1826 /* Samples */
1827 *blockCache++ = l_sample2;
1828 *blockCache++ = r_sample2;
1829 *blockCache++ = l_sample1;
1830 *blockCache++ = r_sample1;
1831 for (i = 0; i < align; i += 1, *buf += 1)
1833 *blockCache++ = FAudio_INTERNAL_ParseNibble(
1834 *(*buf) >> 4,
1835 l_predictor,
1836 &l_delta,
1837 &l_sample1,
1838 &l_sample2
1840 *blockCache++ = FAudio_INTERNAL_ParseNibble(
1841 *(*buf) & 0x0F,
1842 r_predictor,
1843 &r_delta,
1844 &r_sample1,
1845 &r_sample2
1850 #undef READ
1852 void FAudio_INTERNAL_DecodeMonoMSADPCM(
1853 FAudioVoice *voice,
1854 FAudioBuffer *buffer,
1855 float *decodeCache,
1856 uint32_t samples
1858 /* Loop variables */
1859 uint32_t copy, done = 0;
1861 /* Read pointers */
1862 uint8_t *buf;
1863 int32_t midOffset;
1865 /* PCM block cache */
1866 int16_t *blockCache;
1868 /* Block size */
1869 uint32_t bsize = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
1871 LOG_FUNC_ENTER(voice->audio)
1873 /* Where are we starting? */
1874 buf = (uint8_t*) buffer->pAudioData + (
1875 (voice->src.curBufferOffset / bsize) *
1876 voice->src.format->nBlockAlign
1879 /* Are we starting in the middle? */
1880 midOffset = (voice->src.curBufferOffset % bsize);
1882 /* Read in each block directly to the decode cache */
1883 blockCache = (int16_t*) FAudio_alloca(bsize * sizeof(int16_t));
1884 while (done < samples)
1886 copy = FAudio_min(samples - done, bsize - midOffset);
1887 FAudio_INTERNAL_DecodeMonoMSADPCMBlock(
1888 &buf,
1889 blockCache,
1890 voice->src.format->nBlockAlign
1892 FAudio_INTERNAL_Convert_S16_To_F32(
1893 blockCache + midOffset,
1894 decodeCache,
1895 copy
1897 decodeCache += copy;
1898 done += copy;
1899 midOffset = 0;
1901 FAudio_dealloca(blockCache);
1902 LOG_FUNC_EXIT(voice->audio)
1905 void FAudio_INTERNAL_DecodeStereoMSADPCM(
1906 FAudioVoice *voice,
1907 FAudioBuffer *buffer,
1908 float *decodeCache,
1909 uint32_t samples
1911 /* Loop variables */
1912 uint32_t copy, done = 0;
1914 /* Read pointers */
1915 uint8_t *buf;
1916 int32_t midOffset;
1918 /* PCM block cache */
1919 int16_t *blockCache;
1921 /* Align, block size */
1922 uint32_t bsize = ((FAudioADPCMWaveFormat*) voice->src.format)->wSamplesPerBlock;
1924 LOG_FUNC_ENTER(voice->audio)
1926 /* Where are we starting? */
1927 buf = (uint8_t*) buffer->pAudioData + (
1928 (voice->src.curBufferOffset / bsize) *
1929 voice->src.format->nBlockAlign
1932 /* Are we starting in the middle? */
1933 midOffset = (voice->src.curBufferOffset % bsize);
1935 /* Read in each block directly to the decode cache */
1936 blockCache = (int16_t*) FAudio_alloca(bsize * 2 * sizeof(int16_t));
1937 while (done < samples)
1939 copy = FAudio_min(samples - done, bsize - midOffset);
1940 FAudio_INTERNAL_DecodeStereoMSADPCMBlock(
1941 &buf,
1942 blockCache,
1943 voice->src.format->nBlockAlign
1945 FAudio_INTERNAL_Convert_S16_To_F32(
1946 blockCache + (midOffset * 2),
1947 decodeCache,
1948 copy * 2
1950 decodeCache += copy * 2;
1951 done += copy;
1952 midOffset = 0;
1954 FAudio_dealloca(blockCache);
1955 LOG_FUNC_EXIT(voice->audio)
1958 /* Fallback WMA decoder, get ready for spam! */
1960 void FAudio_INTERNAL_DecodeWMAERROR(
1961 FAudioVoice *voice,
1962 FAudioBuffer *buffer,
1963 float *decodeCache,
1964 uint32_t samples
1966 LOG_FUNC_ENTER(voice->audio)
1967 LOG_ERROR(voice->audio, "%s", "WMA IS NOT SUPPORTED IN THIS BUILD!")
1968 FAudio_zero(decodeCache, samples * voice->src.format->nChannels * sizeof(float));
1969 LOG_FUNC_EXIT(voice->audio)
1972 /* vim: set noexpandtab shiftwidth=8 tabstop=8: */