/* FAudio - XAudio Reimplementation for FNA * * Copyright (c) 2011-2020 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 * */ #include "FAudio.h" #include "FAPOBase.h" #include #ifdef FAUDIO_UNKNOWN_PLATFORM #include #include #include #include #include #include #define FAudio_malloc malloc #define FAudio_realloc realloc #define FAudio_free free #define FAudio_alloca(x) alloca(uint8_t, x) #define FAudio_dealloca(x) dealloca(x) #define FAudio_zero(ptr, size) memset(ptr, '\0', size) #define FAudio_memset(ptr, val, size) memset(ptr, val, size) #define FAudio_memcpy(dst, src, size) memcpy(dst, src, size) #define FAudio_memmove(dst, src, size) memmove(dst, src, size) #define FAudio_memcmp(ptr1, ptr2, size) memcmp(ptr1, ptr2, size) #define FAudio_strlen(ptr) strlen(ptr) #define FAudio_strcmp(str1, str2) strcmp(str1, str2) #define FAudio_strlcpy(ptr1, ptr2, size) strlcpy(ptr1, ptr2, size) #define FAudio_pow(x, y) pow(x, y) #define FAudio_log(x) log(x) #define FAudio_log10(x) log10(x) #define FAudio_sin(x) sin(x) #define FAudio_cos(x) cos(x) #define FAudio_tan(x) tan(x) #define FAudio_acos(x) acos(x) #define FAudio_ceil(x) ceil(x) #define FAudio_floor(x) floor(x) #define FAudio_abs(x) abs(x) #define FAudio_ldexp(v, e) ldexp(v, e) #define FAudio_exp(x) exp(x) #define FAudio_cosf(x) cosf(x) #define FAudio_sinf(x) sinf(x) #define FAudio_sqrtf(x) sqrtf(x) #define FAudio_acosf(x) acosf(x) #define FAudio_atan2f(y, x) atan2f(y, x) #define FAudio_fabsf(x) fabsf(x) #define FAudio_qsort qsort #define FAudio_assert assert #define FAudio_snprintf snprintf #define FAudio_vsnprintf vsnprintf #define FAudio_Log(msg) fprintf(stderr, "%s\n", msg) #define FAudio_getenv getenv #define FAudio_PRIu64 PRIu64 #define FAudio_PRIx64 PRIx64 #else #include #include #include #define FAudio_malloc SDL_malloc #define FAudio_realloc SDL_realloc #define FAudio_free SDL_free #define FAudio_alloca(x) SDL_stack_alloc(uint8_t, x) #define FAudio_dealloca(x) SDL_stack_free(x) #define FAudio_zero(ptr, size) SDL_memset(ptr, '\0', size) #define FAudio_memset(ptr, val, size) SDL_memset(ptr, val, size) #define FAudio_memcpy(dst, src, size) SDL_memcpy(dst, src, size) #define FAudio_memmove(dst, src, size) SDL_memmove(dst, src, size) #define FAudio_memcmp(ptr1, ptr2, size) SDL_memcmp(ptr1, ptr2, size) #define FAudio_strlen(ptr) SDL_strlen(ptr) #define FAudio_strcmp(str1, str2) SDL_strcmp(str1, str2) #define FAudio_strlcpy(ptr1, ptr2, size) SDL_strlcpy(ptr1, ptr2, size) #define FAudio_pow(x, y) SDL_pow(x, y) #define FAudio_log(x) SDL_log(x) #define FAudio_log10(x) SDL_log10(x) #define FAudio_sin(x) SDL_sin(x) #define FAudio_cos(x) SDL_cos(x) #define FAudio_tan(x) SDL_tan(x) #define FAudio_acos(x) SDL_acos(x) #define FAudio_ceil(x) SDL_ceil(x) #define FAudio_floor(x) SDL_floor(x) #define FAudio_abs(x) SDL_abs(x) #define FAudio_ldexp(v, e) SDL_scalbn(v, e) #define FAudio_exp(x) SDL_exp(x) #define FAudio_cosf(x) SDL_cosf(x) #define FAudio_sinf(x) SDL_sinf(x) #define FAudio_sqrtf(x) SDL_sqrtf(x) #define FAudio_acosf(x) SDL_acosf(x) #define FAudio_atan2f(y, x) SDL_atan2f(y, x) #define FAudio_fabsf(x) SDL_fabsf(x) #define FAudio_qsort SDL_qsort #ifdef FAUDIO_LOG_ASSERTIONS #define FAudio_assert(condition) \ { \ static uint8_t logged = 0; \ if (!(condition) && !logged) \ { \ SDL_Log("Assertion failed: %s", #condition); \ logged = 1; \ } \ } #else #define FAudio_assert SDL_assert #endif #define FAudio_snprintf SDL_snprintf #define FAudio_vsnprintf SDL_vsnprintf #define FAudio_Log(msg) SDL_Log("%s", msg) #define FAudio_getenv SDL_getenv #define FAudio_PRIu64 SDL_PRIu64 #define FAudio_PRIx64 SDL_PRIx64 #endif /* Easy Macros */ #define FAudio_min(val1, val2) \ (val1 < val2 ? val1 : val2) #define FAudio_max(val1, val2) \ (val1 > val2 ? val1 : val2) #define FAudio_clamp(val, min, max) \ (val > max ? max : (val < min ? min : val)) /* Windows/Visual Studio cruft */ #ifdef _WIN32 #ifdef __cplusplus /* C++ should have `inline`, but not `restrict` */ #define restrict #else #define inline __inline #if defined(_MSC_VER) #if (_MSC_VER >= 1700) /* VS2012+ */ #define restrict __restrict #else /* VS2010- */ #define restrict #endif #endif #endif #endif /* C++ does not have restrict (though VS2012+ does have __restrict) */ #if defined(__cplusplus) && !defined(restrict) #define restrict #endif /* Threading Types */ typedef void* FAudioThread; typedef void* FAudioMutex; typedef int32_t (FAUDIOCALL * FAudioThreadFunc)(void* data); typedef enum FAudioThreadPriority { FAUDIO_THREAD_PRIORITY_LOW, FAUDIO_THREAD_PRIORITY_NORMAL, FAUDIO_THREAD_PRIORITY_HIGH, } FAudioThreadPriority; /* Linked Lists */ typedef struct LinkedList LinkedList; struct LinkedList { void* entry; LinkedList *next; }; void LinkedList_AddEntry( LinkedList **start, void* toAdd, FAudioMutex lock, FAudioMallocFunc pMalloc ); void LinkedList_PrependEntry( LinkedList **start, void* toAdd, FAudioMutex lock, FAudioMallocFunc pMalloc ); void LinkedList_RemoveEntry( LinkedList **start, void* toRemove, FAudioMutex lock, FAudioFreeFunc pFree ); /* Internal FAudio Types */ typedef enum FAudioVoiceType { FAUDIO_VOICE_SOURCE, FAUDIO_VOICE_SUBMIX, FAUDIO_VOICE_MASTER } FAudioVoiceType; typedef struct FAudioBufferEntry FAudioBufferEntry; struct FAudioBufferEntry { FAudioBuffer buffer; FAudioBufferWMA bufferWMA; FAudioBufferEntry *next; }; typedef void (FAUDIOCALL * FAudioDecodeCallback)( FAudioVoice *voice, FAudioBuffer *buffer, /* Buffer to decode */ float *decodeCache, /* Decode into here */ uint32_t samples /* Samples to decode */ ); typedef void (FAUDIOCALL * FAudioResampleCallback)( float *restrict dCache, float *restrict resampleCache, uint64_t *resampleOffset, uint64_t resampleStep, uint64_t toResample, uint8_t channels ); typedef void (FAUDIOCALL * FAudioMixCallback)( uint32_t toMix, uint32_t srcChans, uint32_t dstChans, float baseVolume, float *restrict srcData, float *restrict dstData, float *restrict channelVolume, float *restrict coefficients ); typedef float FAudioFilterState[4]; /* Operation Sets, original implementation by Tyler Glaiel */ typedef struct FAudio_OPERATIONSET_Operation FAudio_OPERATIONSET_Operation; void FAudio_OPERATIONSET_Commit(FAudio *audio, uint32_t OperationSet); void FAudio_OPERATIONSET_CommitAll(FAudio *audio); void FAudio_OPERATIONSET_Execute(FAudio *audio); void FAudio_OPERATIONSET_ClearAll(FAudio *audio); void FAudio_OPERATIONSET_ClearAllForVoice(FAudioVoice *voice); void FAudio_OPERATIONSET_QueueEnableEffect( FAudioVoice *voice, uint32_t EffectIndex, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueDisableEffect( FAudioVoice *voice, uint32_t EffectIndex, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueSetEffectParameters( FAudioVoice *voice, uint32_t EffectIndex, const void *pParameters, uint32_t ParametersByteSize, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueSetFilterParameters( FAudioVoice *voice, const FAudioFilterParameters *pParameters, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueSetOutputFilterParameters( FAudioVoice *voice, FAudioVoice *pDestinationVoice, const FAudioFilterParameters *pParameters, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueSetVolume( FAudioVoice *voice, float Volume, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueSetChannelVolumes( FAudioVoice *voice, uint32_t Channels, const float *pVolumes, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueSetOutputMatrix( FAudioVoice *voice, FAudioVoice *pDestinationVoice, uint32_t SourceChannels, uint32_t DestinationChannels, const float *pLevelMatrix, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueStart( FAudioSourceVoice *voice, uint32_t Flags, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueStop( FAudioSourceVoice *voice, uint32_t Flags, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueExitLoop( FAudioSourceVoice *voice, uint32_t OperationSet ); void FAudio_OPERATIONSET_QueueSetFrequencyRatio( FAudioSourceVoice *voice, float Ratio, uint32_t OperationSet ); /* Public FAudio Types */ struct FAudio { uint8_t version; uint8_t active; uint32_t refcount; uint32_t initFlags; uint32_t updateSize; FAudioMasteringVoice *master; LinkedList *sources; LinkedList *submixes; LinkedList *callbacks; FAudioMutex sourceLock; FAudioMutex submixLock; FAudioMutex callbackLock; FAudioMutex operationLock; FAudioWaveFormatExtensible mixFormat; FAudio_OPERATIONSET_Operation *queuedOperations; FAudio_OPERATIONSET_Operation *committedOperations; /* Used to prevent destroying an active voice */ FAudioSourceVoice *processingSource; /* Temp storage for processing, interleaved PCM32F */ #define EXTRA_DECODE_PADDING 2 uint32_t decodeSamples; uint32_t resampleSamples; uint32_t effectChainSamples; float *decodeCache; float *resampleCache; float *effectChainCache; /* Allocator callbacks */ FAudioMallocFunc pMalloc; FAudioFreeFunc pFree; FAudioReallocFunc pRealloc; /* EngineProcedureEXT */ void *clientEngineUser; FAudioEngineProcedureEXT pClientEngineProc; #ifndef FAUDIO_DISABLE_DEBUGCONFIGURATION /* Debug Information */ FAudioDebugConfiguration debug; #endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */ /* Platform opaque pointer */ void *platform; }; struct FAudioVoice { FAudio *audio; uint32_t flags; FAudioVoiceType type; FAudioVoiceSends sends; float **sendCoefficients; FAudioMixCallback *sendMix; FAudioFilterParameters *sendFilter; FAudioFilterState **sendFilterState; struct { uint32_t count; FAudioEffectDescriptor *desc; void **parameters; uint32_t *parameterSizes; uint8_t *parameterUpdates; uint8_t *inPlaceProcessing; } effects; FAudioFilterParameters filter; FAudioFilterState *filterState; FAudioMutex sendLock; FAudioMutex effectLock; FAudioMutex filterLock; float volume; float *channelVolume; uint32_t outputChannels; FAudioMutex volumeLock; FAUDIONAMELESS union { struct { /* Sample storage */ uint32_t decodeSamples; uint32_t resampleSamples; /* Resampler */ float resampleFreq; uint64_t resampleStep; uint64_t resampleOffset; uint64_t curBufferOffsetDec; uint32_t curBufferOffset; /* FFmpeg */ #ifdef HAVE_FFMPEG struct FAudioFFmpeg *ffmpeg; #endif /* HAVE_FFMPEG*/ /* Read-only */ float maxFreqRatio; FAudioWaveFormatEx *format; FAudioDecodeCallback decode; FAudioResampleCallback resample; FAudioVoiceCallback *callback; /* Dynamic */ uint8_t active; float freqRatio; uint8_t newBuffer; uint64_t totalSamples; FAudioBufferEntry *bufferList; FAudioBufferEntry *flushList; FAudioMutex bufferLock; } src; struct { /* Sample storage */ uint32_t inputSamples; uint32_t outputSamples; float *inputCache; uint64_t resampleStep; FAudioResampleCallback resample; /* Read-only */ uint32_t inputChannels; uint32_t inputSampleRate; uint32_t processingStage; } mix; struct { /* Output stream, allocated by Platform */ float *output; /* Needed when inputChannels != outputChannels */ float *effectCache; /* Read-only */ uint32_t inputChannels; uint32_t inputSampleRate; } master; }; }; /* Internal Functions */ void FAudio_INTERNAL_InsertSubmixSorted( LinkedList **start, FAudioSubmixVoice *toAdd, FAudioMutex lock, FAudioMallocFunc pMalloc ); void FAudio_INTERNAL_UpdateEngine(FAudio *audio, float *output); void FAudio_INTERNAL_ResizeDecodeCache(FAudio *audio, uint32_t size); void FAudio_INTERNAL_ResizeResampleCache(FAudio *audio, uint32_t size); void FAudio_INTERNAL_ResizeEffectChainCache(FAudio *audio, uint32_t samples); void FAudio_INTERNAL_AllocEffectChain( FAudioVoice *voice, const FAudioEffectChain *pEffectChain ); void FAudio_INTERNAL_FreeEffectChain(FAudioVoice *voice); uint32_t FAudio_INTERNAL_VoiceOutputFrequency( FAudioVoice *voice, const FAudioVoiceSends *pSendList ); extern const float FAUDIO_INTERNAL_MATRIX_DEFAULTS[8][8][64]; /* Debug */ #ifdef FAUDIO_DISABLE_DEBUGCONFIGURATION #define LOG_ERROR(engine, fmt, ...) #define LOG_WARNING(engine, fmt, ...) #define LOG_INFO(engine, fmt, ...) #define LOG_DETAIL(engine, fmt, ...) #define LOG_API_ENTER(engine) #define LOG_API_EXIT(engine) #define LOG_FUNC_ENTER(engine) #define LOG_FUNC_EXIT(engine) /* TODO: LOG_TIMING */ #define LOG_MUTEX_CREATE(engine, mutex) #define LOG_MUTEX_DESTROY(engine, mutex) #define LOG_MUTEX_LOCK(engine, mutex) #define LOG_MUTEX_UNLOCK(engine, mutex) /* TODO: LOG_MEMORY */ /* TODO: LOG_STREAMING */ #define LOG_FORMAT(engine, waveFormat) #else #if defined(_MSC_VER) /* VC doesn't support __attribute__ at all, and there's no replacement for format. */ void FAudio_INTERNAL_debug( FAudio *audio, const char *file, uint32_t line, const char *func, const char *fmt, ... ); #if _MSC_VER <= 1700 /* <=2012 also doesn't support __func__ */ #define __func__ __FUNCTION__ #endif #else void FAudio_INTERNAL_debug( FAudio *audio, const char *file, uint32_t line, const char *func, const char *fmt, ... ) __attribute__((format(printf,5,6))); #endif void FAudio_INTERNAL_debug_fmt( FAudio *audio, const char *file, uint32_t line, const char *func, const FAudioWaveFormatEx *fmt ); #define PRINT_DEBUG(engine, cond, type, fmt, ...) \ if (engine->debug.TraceMask & FAUDIO_LOG_##cond) \ { \ FAudio_INTERNAL_debug( \ engine, \ __FILE__, \ __LINE__, \ __func__, \ type ": " fmt, \ __VA_ARGS__ \ ); \ } #define LOG_ERROR(engine, fmt, ...) PRINT_DEBUG(engine, ERRORS, "ERROR", fmt, __VA_ARGS__) #define LOG_WARNING(engine, fmt, ...) PRINT_DEBUG(engine, WARNINGS, "WARNING", fmt, __VA_ARGS__) #define LOG_INFO(engine, fmt, ...) PRINT_DEBUG(engine, INFO, "INFO", fmt, __VA_ARGS__) #define LOG_DETAIL(engine, fmt, ...) PRINT_DEBUG(engine, DETAIL, "DETAIL", fmt, __VA_ARGS__) #define LOG_API_ENTER(engine) PRINT_DEBUG(engine, API_CALLS, "API Enter", "%s", __func__) #define LOG_API_EXIT(engine) PRINT_DEBUG(engine, API_CALLS, "API Exit", "%s", __func__) #define LOG_FUNC_ENTER(engine) PRINT_DEBUG(engine, FUNC_CALLS, "FUNC Enter", "%s", __func__) #define LOG_FUNC_EXIT(engine) PRINT_DEBUG(engine, FUNC_CALLS, "FUNC Exit", "%s", __func__) /* TODO: LOG_TIMING */ #define LOG_MUTEX_CREATE(engine, mutex) PRINT_DEBUG(engine, LOCKS, "Mutex Create", "%p", mutex) #define LOG_MUTEX_DESTROY(engine, mutex) PRINT_DEBUG(engine, LOCKS, "Mutex Destroy", "%p", mutex) #define LOG_MUTEX_LOCK(engine, mutex) PRINT_DEBUG(engine, LOCKS, "Mutex Lock", "%p", mutex) #define LOG_MUTEX_UNLOCK(engine, mutex) PRINT_DEBUG(engine, LOCKS, "Mutex Unlock", "%p", mutex) /* TODO: LOG_MEMORY */ /* TODO: LOG_STREAMING */ #define LOG_FORMAT(engine, waveFormat) \ if (engine->debug.TraceMask & FAUDIO_LOG_INFO) \ { \ FAudio_INTERNAL_debug_fmt( \ engine, \ __FILE__, \ __LINE__, \ __func__, \ waveFormat \ ); \ } #endif /* FAUDIO_DISABLE_DEBUGCONFIGURATION */ /* FAPOFX Creators */ #define CREATE_FAPOFX_FUNC(effect) \ extern uint32_t FAPOFXCreate##effect( \ FAPO **pEffect, \ const void *pInitData, \ uint32_t InitDataByteSize, \ FAudioMallocFunc customMalloc, \ FAudioFreeFunc customFree, \ FAudioReallocFunc customRealloc, \ uint8_t legacy \ ); CREATE_FAPOFX_FUNC(EQ) CREATE_FAPOFX_FUNC(MasteringLimiter) CREATE_FAPOFX_FUNC(Reverb) CREATE_FAPOFX_FUNC(Echo) #undef CREATE_FAPOFX_FUNC /* SIMD Stuff */ /* Callbacks declared as functions (rather than function pointers) are * scalar-only, for now. SIMD versions should be possible for these. */ extern void (*FAudio_INTERNAL_Convert_U8_To_F32)( const uint8_t *restrict src, float *restrict dst, uint32_t len ); extern void (*FAudio_INTERNAL_Convert_S16_To_F32)( const int16_t *restrict src, float *restrict dst, uint32_t len ); extern void (*FAudio_INTERNAL_Convert_S32_To_F32)( const int32_t *restrict src, float *restrict dst, uint32_t len ); extern FAudioResampleCallback FAudio_INTERNAL_ResampleMono; extern FAudioResampleCallback FAudio_INTERNAL_ResampleStereo; extern void FAudio_INTERNAL_ResampleGeneric( float *restrict dCache, float *restrict resampleCache, uint64_t *resampleOffset, uint64_t resampleStep, uint64_t toResample, uint8_t channels ); extern void (*FAudio_INTERNAL_Amplify)( float *output, uint32_t totalSamples, float volume ); #define MIX_FUNC(type) \ extern void FAudio_INTERNAL_Mix_##type##_Scalar( \ uint32_t toMix, \ uint32_t srcChans, \ uint32_t dstChans, \ float baseVolume, \ float *restrict srcData, \ float *restrict dstData, \ float *restrict channelVolume, \ float *restrict coefficients \ ); MIX_FUNC(Generic) MIX_FUNC(1in_1out) MIX_FUNC(1in_2out) MIX_FUNC(1in_6out) MIX_FUNC(1in_8out) MIX_FUNC(2in_1out) MIX_FUNC(2in_2out) MIX_FUNC(2in_6out) MIX_FUNC(2in_8out) #undef MIX_FUNC void FAudio_INTERNAL_InitSIMDFunctions(uint8_t hasSSE2, uint8_t hasNEON); /* Decoders */ #define DECODE_FUNC(type) \ extern void FAudio_INTERNAL_Decode##type( \ FAudioVoice *voice, \ FAudioBuffer *buffer, \ float *decodeCache, \ uint32_t samples \ ); DECODE_FUNC(PCM8) DECODE_FUNC(PCM16) DECODE_FUNC(PCM24) DECODE_FUNC(PCM32) DECODE_FUNC(PCM32F) DECODE_FUNC(MonoMSADPCM) DECODE_FUNC(StereoMSADPCM) DECODE_FUNC(WMAERROR) #ifdef HAVE_FFMPEG DECODE_FUNC(FFMPEG) #endif /* HAVE_FFMPEG */ #undef DECODE_FUNC /* FFmpeg */ #ifdef HAVE_FFMPEG uint32_t FAudio_FFMPEG_init(FAudioSourceVoice *pSourceVoice, uint32_t type); void FAudio_FFMPEG_free(FAudioSourceVoice *voice); void FAudio_FFMPEG_reset(FAudioSourceVoice *voice); #endif /* HAVE_FFMPEG */ /* Platform Functions */ void FAudio_PlatformAddRef(void); void FAudio_PlatformRelease(void); void FAudio_PlatformInit( FAudio *audio, uint32_t flags, uint32_t deviceIndex, FAudioWaveFormatExtensible *mixFormat, uint32_t *updateSize, void** platformDevice ); void FAudio_PlatformQuit(void* platformDevice); uint32_t FAudio_PlatformGetDeviceCount(void); uint32_t FAudio_PlatformGetDeviceDetails( uint32_t index, FAudioDeviceDetails *details ); /* Threading */ FAudioThread FAudio_PlatformCreateThread( FAudioThreadFunc func, const char *name, void* data ); void FAudio_PlatformWaitThread(FAudioThread thread, int32_t *retval); void FAudio_PlatformThreadPriority(FAudioThreadPriority priority); uint64_t FAudio_PlatformGetThreadID(void); FAudioMutex FAudio_PlatformCreateMutex(void); void FAudio_PlatformDestroyMutex(FAudioMutex mutex); void FAudio_PlatformLockMutex(FAudioMutex mutex); void FAudio_PlatformUnlockMutex(FAudioMutex mutex); void FAudio_sleep(uint32_t ms); /* Time */ uint32_t FAudio_timems(void); /* WaveFormatExtensible Helpers */ static inline uint32_t GetMask(uint16_t channels) { if (channels == 1) return SPEAKER_MONO; if (channels == 2) return SPEAKER_STEREO; if (channels == 3) return SPEAKER_2POINT1; if (channels == 4) return SPEAKER_QUAD; if (channels == 5) return SPEAKER_4POINT1; if (channels == 6) return SPEAKER_5POINT1; if (channels == 8) return SPEAKER_7POINT1; FAudio_assert(0 && "Unrecognized speaker layout!"); return 0; } static inline void WriteWaveFormatExtensible( FAudioWaveFormatExtensible *fmt, int channels, int samplerate ) { FAudio_assert(fmt != NULL); fmt->Format.wBitsPerSample = 32; fmt->Format.wFormatTag = FAUDIO_FORMAT_EXTENSIBLE; fmt->Format.nChannels = channels; fmt->Format.nSamplesPerSec = samplerate; fmt->Format.nBlockAlign = ( fmt->Format.nChannels * (fmt->Format.wBitsPerSample / 8) ); fmt->Format.nAvgBytesPerSec = ( fmt->Format.nSamplesPerSec * fmt->Format.nBlockAlign ); fmt->Format.cbSize = sizeof(FAudioWaveFormatExtensible) - sizeof(FAudioWaveFormatEx); fmt->Samples.wValidBitsPerSample = 32; fmt->dwChannelMask = GetMask(fmt->Format.nChannels); FAudio_memcpy(&fmt->SubFormat, &DATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(FAudioGUID)); } /* Resampling */ /* Okay, so here's what all this fixed-point goo is for: * * Inevitably you're going to run into weird sample rates, * both from WaveBank data and from pitch shifting changes. * * How we deal with this is by calculating a fixed "step" * value that steps from sample to sample at the speed needed * to get the correct output sample rate, and the offset * is stored as separate integer and fraction values. * * This allows us to do weird fractional steps between samples, * while at the same time not letting it drift off into death * thanks to floating point madness. * * Steps are stored in fixed-point with 32 bits for the fraction: * * 00000000000000000000000000000000 00000000000000000000000000000000 * ^ Integer block (32) ^ Fraction block (32) * * For example, to get 1.5: * 00000000000000000000000000000001 10000000000000000000000000000000 * * The Integer block works exactly like you'd expect. * The Fraction block is divided by the Integer's "One" value. * So, the above Fraction represented visually... * 1 << 31 * ------- * 1 << 32 * ... which, simplified, is... * 1 << 0 * ------ * 1 << 1 * ... in other words, 1 / 2, or 0.5. */ #define FIXED_PRECISION 32 #define FIXED_ONE (1LL << FIXED_PRECISION) /* Quick way to drop parts */ #define FIXED_FRACTION_MASK (FIXED_ONE - 1) #define FIXED_INTEGER_MASK ~FIXED_FRACTION_MASK /* Helper macros to convert fixed to float */ #define DOUBLE_TO_FIXED(dbl) \ ((uint64_t) (dbl * FIXED_ONE + 0.5)) #define FIXED_TO_DOUBLE(fxd) ( \ (double) (fxd >> FIXED_PRECISION) + /* Integer part */ \ ((fxd & FIXED_FRACTION_MASK) * (1.0 / FIXED_ONE)) /* Fraction part */ \ ) #define FIXED_TO_FLOAT(fxd) ( \ (float) (fxd >> FIXED_PRECISION) + /* Integer part */ \ ((fxd & FIXED_FRACTION_MASK) * (1.0f / FIXED_ONE)) /* Fraction part */ \ ) #ifdef FAUDIO_DUMP_VOICES /* File writing structure */ typedef size_t (FAUDIOCALL * FAudio_writefunc)( void *data, const void *src, size_t size, size_t count ); typedef size_t (FAUDIOCALL * FAudio_sizefunc)( void *data ); typedef struct FAudioIOStreamOut { void *data; FAudio_readfunc read; FAudio_writefunc write; FAudio_seekfunc seek; FAudio_sizefunc size; FAudio_closefunc close; void *lock; } FAudioIOStreamOut; FAudioIOStreamOut* FAudio_fopen_out(const char *path, const char *mode); void FAudio_close_out(FAudioIOStreamOut *io); #endif /* FAUDIO_DUMP_VOICES */ /* vim: set noexpandtab shiftwidth=8 tabstop=8: */