/* 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 "FAPOBase.h" #include "FAudio_internal.h" /* FAPOBase Interface */ void CreateFAPOBase( FAPOBase *fapo, const FAPORegistrationProperties *pRegistrationProperties, uint8_t *pParameterBlocks, uint32_t uParameterBlockByteSize, uint8_t fProducer ) { CreateFAPOBaseWithCustomAllocatorEXT( fapo, pRegistrationProperties, pParameterBlocks, uParameterBlockByteSize, fProducer, FAudio_malloc, FAudio_free, FAudio_realloc ); } void CreateFAPOBaseWithCustomAllocatorEXT( FAPOBase *fapo, const FAPORegistrationProperties *pRegistrationProperties, uint8_t *pParameterBlocks, uint32_t uParameterBlockByteSize, uint8_t fProducer, FAudioMallocFunc customMalloc, FAudioFreeFunc customFree, FAudioReallocFunc customRealloc ) { /* Base Classes/Interfaces */ #define ASSIGN_VT(name) \ fapo->base.name = (name##Func) FAPOBase_##name; ASSIGN_VT(AddRef) ASSIGN_VT(Release) ASSIGN_VT(GetRegistrationProperties) ASSIGN_VT(IsInputFormatSupported) ASSIGN_VT(IsOutputFormatSupported) ASSIGN_VT(Initialize) ASSIGN_VT(Reset) ASSIGN_VT(LockForProcess) ASSIGN_VT(UnlockForProcess) ASSIGN_VT(CalcInputFrames) ASSIGN_VT(CalcOutputFrames) ASSIGN_VT(SetParameters) ASSIGN_VT(GetParameters) #undef ASSIGN_VT /* Public Virtual Functions */ fapo->OnSetParameters = (OnSetParametersFunc) FAPOBase_OnSetParameters; /* Private Variables */ fapo->m_pRegistrationProperties = pRegistrationProperties; /* FIXME */ fapo->m_pfnMatrixMixFunction = NULL; /* FIXME */ fapo->m_pfl32MatrixCoefficients = NULL; /* FIXME */ fapo->m_nSrcFormatType = 0; /* FIXME */ fapo->m_fIsScalarMatrix = 0; /* FIXME: */ fapo->m_fIsLocked = 0; fapo->m_pParameterBlocks = pParameterBlocks; fapo->m_pCurrentParameters = pParameterBlocks; fapo->m_pCurrentParametersInternal = pParameterBlocks; fapo->m_uCurrentParametersIndex = 0; fapo->m_uParameterBlockByteSize = uParameterBlockByteSize; fapo->m_fNewerResultsReady = 0; fapo->m_fProducer = fProducer; /* Allocator Callbacks */ fapo->pMalloc = customMalloc; fapo->pFree = customFree; fapo->pRealloc = customRealloc; /* Protected Variables */ fapo->m_lReferenceCount = 1; } int32_t FAPOBase_AddRef(FAPOBase *fapo) { fapo->m_lReferenceCount += 1; return fapo->m_lReferenceCount; } int32_t FAPOBase_Release(FAPOBase *fapo) { fapo->m_lReferenceCount -= 1; if (fapo->m_lReferenceCount == 0) { fapo->Destructor(fapo); return 0; } return fapo->m_lReferenceCount; } uint32_t FAPOBase_GetRegistrationProperties( FAPOBase *fapo, FAPORegistrationProperties **ppRegistrationProperties ) { *ppRegistrationProperties = (FAPORegistrationProperties*) fapo->pMalloc( sizeof(FAPORegistrationProperties) ); FAudio_memcpy( *ppRegistrationProperties, fapo->m_pRegistrationProperties, sizeof(FAPORegistrationProperties) ); return 0; } uint32_t FAPOBase_IsInputFormatSupported( FAPOBase *fapo, const FAudioWaveFormatEx *pOutputFormat, const FAudioWaveFormatEx *pRequestedInputFormat, FAudioWaveFormatEx **ppSupportedInputFormat ) { if ( pRequestedInputFormat->wFormatTag != FAPOBASE_DEFAULT_FORMAT_TAG || pRequestedInputFormat->nChannels < FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS || pRequestedInputFormat->nChannels > FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS || pRequestedInputFormat->nSamplesPerSec < FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE || pRequestedInputFormat->nSamplesPerSec > FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE || pRequestedInputFormat->wBitsPerSample != FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE ) { if (ppSupportedInputFormat != NULL) { (*ppSupportedInputFormat)->wFormatTag = FAPOBASE_DEFAULT_FORMAT_TAG; (*ppSupportedInputFormat)->nChannels = FAudio_clamp( pRequestedInputFormat->nChannels, FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS, FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS ); (*ppSupportedInputFormat)->nSamplesPerSec = FAudio_clamp( pRequestedInputFormat->nSamplesPerSec, FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE, FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE ); (*ppSupportedInputFormat)->wBitsPerSample = FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE; } return FAPO_E_FORMAT_UNSUPPORTED; } return 0; } uint32_t FAPOBase_IsOutputFormatSupported( FAPOBase *fapo, const FAudioWaveFormatEx *pInputFormat, const FAudioWaveFormatEx *pRequestedOutputFormat, FAudioWaveFormatEx **ppSupportedOutputFormat ) { if ( pRequestedOutputFormat->wFormatTag != FAPOBASE_DEFAULT_FORMAT_TAG || pRequestedOutputFormat->nChannels < FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS || pRequestedOutputFormat->nChannels > FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS || pRequestedOutputFormat->nSamplesPerSec < FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE || pRequestedOutputFormat->nSamplesPerSec > FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE || pRequestedOutputFormat->wBitsPerSample != FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE ) { if (ppSupportedOutputFormat != NULL) { (*ppSupportedOutputFormat)->wFormatTag = FAPOBASE_DEFAULT_FORMAT_TAG; (*ppSupportedOutputFormat)->nChannels = FAudio_clamp( pRequestedOutputFormat->nChannels, FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS, FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS ); (*ppSupportedOutputFormat)->nSamplesPerSec = FAudio_clamp( pRequestedOutputFormat->nSamplesPerSec, FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE, FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE ); (*ppSupportedOutputFormat)->wBitsPerSample = FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE; } return FAPO_E_FORMAT_UNSUPPORTED; } return 0; } uint32_t FAPOBase_Initialize( FAPOBase *fapo, const void* pData, uint32_t DataByteSize ) { return 0; } void FAPOBase_Reset(FAPOBase *fapo) { } uint32_t FAPOBase_LockForProcess( FAPOBase *fapo, uint32_t InputLockedParameterCount, const FAPOLockForProcessBufferParameters *pInputLockedParameters, uint32_t OutputLockedParameterCount, const FAPOLockForProcessBufferParameters *pOutputLockedParameters ) { /* Verify parameter counts... */ if ( InputLockedParameterCount < fapo->m_pRegistrationProperties->MinInputBufferCount || InputLockedParameterCount > fapo->m_pRegistrationProperties->MaxInputBufferCount || OutputLockedParameterCount < fapo->m_pRegistrationProperties->MinOutputBufferCount || OutputLockedParameterCount > fapo->m_pRegistrationProperties->MaxOutputBufferCount ) { return FAUDIO_E_INVALID_ARG; } /* Validate input/output formats */ #define VERIFY_FORMAT_FLAG(flag, prop) \ if ( (fapo->m_pRegistrationProperties->Flags & flag) && \ (pInputLockedParameters->pFormat->prop != pOutputLockedParameters->pFormat->prop) ) \ { \ return FAUDIO_E_INVALID_ARG; \ } VERIFY_FORMAT_FLAG(FAPO_FLAG_CHANNELS_MUST_MATCH, nChannels) VERIFY_FORMAT_FLAG(FAPO_FLAG_FRAMERATE_MUST_MATCH, nSamplesPerSec) VERIFY_FORMAT_FLAG(FAPO_FLAG_BITSPERSAMPLE_MUST_MATCH, wBitsPerSample) #undef VERIFY_FORMAT_FLAG if ( (fapo->m_pRegistrationProperties->Flags & FAPO_FLAG_BUFFERCOUNT_MUST_MATCH) && (InputLockedParameterCount != OutputLockedParameterCount) ) { return FAUDIO_E_INVALID_ARG; } fapo->m_fIsLocked = 1; return 0; } void FAPOBase_UnlockForProcess(FAPOBase *fapo) { fapo->m_fIsLocked = 0; } uint32_t FAPOBase_CalcInputFrames(FAPOBase *fapo, uint32_t OutputFrameCount) { return OutputFrameCount; } uint32_t FAPOBase_CalcOutputFrames(FAPOBase *fapo, uint32_t InputFrameCount) { return InputFrameCount; } uint32_t FAPOBase_ValidateFormatDefault( FAPOBase *fapo, FAudioWaveFormatEx *pFormat, uint8_t fOverwrite ) { if ( pFormat->wFormatTag != FAPOBASE_DEFAULT_FORMAT_TAG || pFormat->nChannels < FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS || pFormat->nChannels > FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS || pFormat->nSamplesPerSec < FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE || pFormat->nSamplesPerSec > FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE || pFormat->wBitsPerSample != FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE ) { if (fOverwrite) { pFormat->wFormatTag = FAPOBASE_DEFAULT_FORMAT_TAG; pFormat->nChannels = FAudio_clamp( pFormat->nChannels, FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS, FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS ); pFormat->nSamplesPerSec = FAudio_clamp( pFormat->nSamplesPerSec, FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE, FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE ); pFormat->wBitsPerSample = FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE; } return FAPO_E_FORMAT_UNSUPPORTED; } return 0; } uint32_t FAPOBase_ValidateFormatPair( FAPOBase *fapo, const FAudioWaveFormatEx *pSupportedFormat, FAudioWaveFormatEx *pRequestedFormat, uint8_t fOverwrite ) { if ( pRequestedFormat->wFormatTag != FAPOBASE_DEFAULT_FORMAT_TAG || pRequestedFormat->nChannels < FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS || pRequestedFormat->nChannels > FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS || pRequestedFormat->nSamplesPerSec < FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE || pRequestedFormat->nSamplesPerSec > FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE || pRequestedFormat->wBitsPerSample != FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE ) { if (fOverwrite) { pRequestedFormat->wFormatTag = FAPOBASE_DEFAULT_FORMAT_TAG; pRequestedFormat->nChannels = FAudio_clamp( pRequestedFormat->nChannels, FAPOBASE_DEFAULT_FORMAT_MIN_CHANNELS, FAPOBASE_DEFAULT_FORMAT_MAX_CHANNELS ); pRequestedFormat->nSamplesPerSec = FAudio_clamp( pRequestedFormat->nSamplesPerSec, FAPOBASE_DEFAULT_FORMAT_MIN_FRAMERATE, FAPOBASE_DEFAULT_FORMAT_MAX_FRAMERATE ); pRequestedFormat->wBitsPerSample = FAPOBASE_DEFAULT_FORMAT_BITSPERSAMPLE; } return FAPO_E_FORMAT_UNSUPPORTED; } return 0; } void FAPOBase_ProcessThru( FAPOBase *fapo, void* pInputBuffer, float *pOutputBuffer, uint32_t FrameCount, uint16_t InputChannelCount, uint16_t OutputChannelCount, uint8_t MixWithOutput ) { uint32_t i, co, ci; float *input = (float*) pInputBuffer; if (MixWithOutput) { /* TODO: SSE */ for (i = 0; i < FrameCount; i += 1) for (co = 0; co < OutputChannelCount; co += 1) for (ci = 0; ci < InputChannelCount; ci += 1) { /* Add, don't overwrite! */ pOutputBuffer[i * OutputChannelCount + co] += input[i * InputChannelCount + ci]; } } else { /* TODO: SSE */ for (i = 0; i < FrameCount; i += 1) for (co = 0; co < OutputChannelCount; co += 1) for (ci = 0; ci < InputChannelCount; ci += 1) { /* Overwrite, don't add! */ pOutputBuffer[i * OutputChannelCount + co] = input[i * InputChannelCount + ci]; } } } void FAPOBase_SetParameters( FAPOBase *fapo, const void* pParameters, uint32_t ParameterByteSize ) { FAudio_assert(!fapo->m_fProducer); /* User callback for validation */ fapo->OnSetParameters( fapo, pParameters, ParameterByteSize ); /* Increment parameter block index... */ fapo->m_uCurrentParametersIndex += 1; if (fapo->m_uCurrentParametersIndex == 3) { fapo->m_uCurrentParametersIndex = 0; } fapo->m_pCurrentParametersInternal = fapo->m_pParameterBlocks + ( fapo->m_uParameterBlockByteSize * fapo->m_uCurrentParametersIndex ); /* Copy to what will eventually be the next parameter update */ FAudio_memcpy( fapo->m_pCurrentParametersInternal, pParameters, ParameterByteSize ); } void FAPOBase_GetParameters( FAPOBase *fapo, void* pParameters, uint32_t ParameterByteSize ) { /* Copy what's current as of the last Process */ FAudio_memcpy( pParameters, fapo->m_pCurrentParameters, ParameterByteSize ); } void FAPOBase_OnSetParameters( FAPOBase *fapo, const void* parameters, uint32_t parametersSize ) { } uint8_t FAPOBase_ParametersChanged(FAPOBase *fapo) { /* Internal will get updated when SetParameters is called */ return fapo->m_pCurrentParametersInternal != fapo->m_pCurrentParameters; } uint8_t* FAPOBase_BeginProcess(FAPOBase *fapo) { /* Set the latest block as "current", this is what Process will use now */ fapo->m_pCurrentParameters = fapo->m_pCurrentParametersInternal; return fapo->m_pCurrentParameters; } void FAPOBase_EndProcess(FAPOBase *fapo) { /* I'm 100% sure my parameter block increment is wrong... */ } /* vim: set noexpandtab shiftwidth=8 tabstop=8: */