// // Copyright © 2025 Agora // This file is part of TEN Framework, an open source project. // Licensed under the Apache License, Version 2.0, with certain conditions. // Refer to the "LICENSE" file in the root directory for more information. // #include "biquad.h" #include #include #include #include #include "biquad_st.h" #define AUP_BIQUAD_NUM_DUMP_FILES (20) #define AUP_BIQUAD_DUMP_FILENAMES (200) // ========================================================================================== // internal tools // ========================================================================================== static int AUP_Biquad_checkStatCfg(const Biquad_StaticCfg* pCfg) { int secIdx; if (pCfg == NULL) { return -1; } if (pCfg->maxNSample == 0 || pCfg->maxNSample > AGORA_UAP_BIQUAD_MAX_INPUT_LEN) { return -1; } if (pCfg->nsect > AGORA_UAP_BIQUAD_MAX_SECTION) { return -1; } // if external filter coefficients are required, we need to check the // external filter coeff pointers' validness if (pCfg->nsect > 0) { for (secIdx = 0; secIdx < pCfg->nsect; secIdx++) { if (pCfg->B[secIdx] == NULL || pCfg->A[secIdx] == NULL) { return -1; } } if (pCfg->G == NULL) { return -1; } } return 0; } static int AUP_Biquad_publishStaticCfg(Biquad_St* stHdl) { const Biquad_StaticCfg* pStatCfg; int idx; if (stHdl == NULL) { return -1; } pStatCfg = (const Biquad_StaticCfg*)(&(stHdl->stCfg)); stHdl->maxNSample = (int)pStatCfg->maxNSample; // first, give default (all-pass-filter) values to filter coeffs for (idx = 0; idx < AGORA_UAP_BIQUAD_MAX_SECTION; idx++) { stHdl->BCoeff[idx][0] = 1.0f; stHdl->BCoeff[idx][1] = 0; stHdl->BCoeff[idx][2] = 0; stHdl->ACoeff[idx][0] = 1.0f; stHdl->ACoeff[idx][1] = 0; stHdl->ACoeff[idx][2] = 0; stHdl->GCoeff[idx] = 1.0f; } if (pStatCfg->nsect <= 0) { stHdl->nsect = _BIQUAD_DC_REMOVAL_NSECT; for (idx = 0; idx < stHdl->nsect; idx++) { stHdl->BCoeff[idx][0] = _BIQUAD_DC_REMOVAL_B[idx][0]; stHdl->BCoeff[idx][1] = _BIQUAD_DC_REMOVAL_B[idx][1]; stHdl->BCoeff[idx][2] = _BIQUAD_DC_REMOVAL_B[idx][2]; stHdl->ACoeff[idx][0] = _BIQUAD_DC_REMOVAL_A[idx][0]; stHdl->ACoeff[idx][1] = _BIQUAD_DC_REMOVAL_A[idx][1]; stHdl->ACoeff[idx][2] = _BIQUAD_DC_REMOVAL_A[idx][2]; stHdl->GCoeff[idx] = _BIQUAD_DC_REMOVAL_G[idx]; } } else { stHdl->nsect = pStatCfg->nsect; for (idx = 0; idx < stHdl->nsect; idx++) { stHdl->BCoeff[idx][0] = pStatCfg->B[idx][0]; stHdl->BCoeff[idx][1] = pStatCfg->B[idx][1]; stHdl->BCoeff[idx][2] = pStatCfg->B[idx][2]; stHdl->ACoeff[idx][0] = pStatCfg->A[idx][0]; stHdl->ACoeff[idx][1] = pStatCfg->A[idx][1]; stHdl->ACoeff[idx][2] = pStatCfg->A[idx][2]; stHdl->GCoeff[idx] = pStatCfg->G[idx]; } } return 0; } static int AUP_Biquad_resetVariables(Biquad_St* stHdl) { memset(stHdl->dynamMemPtr, 0, stHdl->dynamMemSize); memset(stHdl->sectW, 0, sizeof(stHdl->sectW)); return 0; } // ========================================================================================== // public APIS // ========================================================================================== int AUP_Biquad_create(void** stPtr) { Biquad_St* tmpPtr; if (stPtr == NULL) { return -1; } *stPtr = (void*)malloc(sizeof(Biquad_St)); if (*stPtr == NULL) { return -1; } memset(*stPtr, 0, sizeof(Biquad_St)); tmpPtr = (Biquad_St*)(*stPtr); tmpPtr->dynamMemPtr = NULL; tmpPtr->dynamMemSize = 0; tmpPtr->stCfg.maxNSample = 768; tmpPtr->stCfg.nsect = 0; for (int idx = 0; idx < AGORA_UAP_BIQUAD_MAX_SECTION; idx++) { tmpPtr->stCfg.A[idx] = NULL; tmpPtr->stCfg.B[idx] = NULL; } tmpPtr->stCfg.G = NULL; return 0; } int AUP_Biquad_destroy(void** stPtr) { Biquad_St* stHdl; if (stPtr == NULL) { return 0; } stHdl = (Biquad_St*)(*stPtr); if (stHdl == NULL) { return 0; } if (stHdl->dynamMemPtr != NULL) { free(stHdl->dynamMemPtr); } stHdl->dynamMemPtr = NULL; free(stHdl); (*stPtr) = NULL; return 0; } int AUP_Biquad_memAllocate(void* stPtr, const Biquad_StaticCfg* pCfg) { Biquad_St* stHdl = NULL; char* memPtr = NULL; int maxNSample, nsect, idx; int inputTempBufMemSize = 0; int sectOutputBufMemSize_EACH = 0; int totalMemSize = 0; if (stPtr == NULL || pCfg == NULL) { return -1; } stHdl = (Biquad_St*)(stPtr); if (AUP_Biquad_checkStatCfg(pCfg) < 0) { return -1; } memcpy(&(stHdl->stCfg), pCfg, sizeof(Biquad_StaticCfg)); if (AUP_Biquad_publishStaticCfg(stHdl) < 0) { return -1; } maxNSample = stHdl->maxNSample; nsect = stHdl->nsect; // check memory requirement inputTempBufMemSize = AGORA_UAP_BIQUAD_ALIGN8(sizeof(float) * maxNSample); totalMemSize += inputTempBufMemSize; sectOutputBufMemSize_EACH = AGORA_UAP_BIQUAD_ALIGN8(sizeof(float) * maxNSample); totalMemSize += sectOutputBufMemSize_EACH * nsect; // allocate dynamic memory if ((size_t)totalMemSize > stHdl->dynamMemSize) { if (stHdl->dynamMemPtr != NULL) { free(stHdl->dynamMemPtr); stHdl->dynamMemSize = 0; } stHdl->dynamMemPtr = malloc(totalMemSize); if (stHdl->dynamMemPtr == NULL) { return -1; } stHdl->dynamMemSize = totalMemSize; } memset(stHdl->dynamMemPtr, 0, stHdl->dynamMemSize); // setup the pointers/variable memPtr = (char*)(stHdl->dynamMemPtr); stHdl->inputTempBuf = (float*)memPtr; memPtr += inputTempBufMemSize; for (idx = 0; idx < nsect; idx++) { stHdl->sectOutputBuf[idx] = (float*)memPtr; memPtr += sectOutputBufMemSize_EACH; } for (; idx < AGORA_UAP_BIQUAD_MAX_SECTION; idx++) { stHdl->sectOutputBuf[idx] = NULL; } if (((int)(memPtr - (char*)(stHdl->dynamMemPtr))) > totalMemSize) { return -1; } return 0; } int AUP_Biquad_init(void* stPtr) { Biquad_St* stHdl; if (stPtr == NULL) { return -1; } stHdl = (Biquad_St*)(stPtr); if (AUP_Biquad_resetVariables(stHdl) < 0) { return -1; } return 0; } int AUP_Biquad_getStaticCfg(const void* stPtr, Biquad_StaticCfg* pCfg) { const Biquad_St* stHdl; if (stPtr == NULL || pCfg == NULL) { return -1; } stHdl = (const Biquad_St*)(stPtr); memcpy(pCfg, &(stHdl->stCfg), sizeof(Biquad_StaticCfg)); return 0; } int AUP_Biquad_getAlgDelay(const void* stPtr, int* delayInSamples) { const Biquad_St* stHdl; if (stPtr == NULL || delayInSamples == NULL) { return -1; } stHdl = (const Biquad_St*)(stPtr); *delayInSamples = stHdl->nsect; return 0; } int AUP_Biquad_proc(void* stPtr, const Biquad_InputData* pIn, Biquad_OutputData* pOut) { Biquad_St* stHdl = NULL; int isFloatIO = 0; int inputNSamples, nSect; int sectIdx, smplIdx; float tmp1; const short* pShortTemp; float* src; float* tgt; if (stPtr == NULL || pIn == NULL || pOut == NULL) { // pCtrl == NULL return -1; } if (pIn->samplesPtr == NULL || pOut->outputBuff == NULL) { return -1; } stHdl = (Biquad_St*)(stPtr); if (((int)pIn->nsamples) > stHdl->maxNSample) { return -1; } isFloatIO = 0; if (pIn->sampleType != 0) { isFloatIO = 1; } inputNSamples = (int)pIn->nsamples; nSect = stHdl->nsect; // special handle for input if (isFloatIO == 0) { pShortTemp = (const short*)pIn->samplesPtr; for (smplIdx = 0; smplIdx < inputNSamples; smplIdx++) { stHdl->inputTempBuf[smplIdx] = (float)pShortTemp[smplIdx]; } } else { memcpy(stHdl->inputTempBuf, (const float*)pIn->samplesPtr, sizeof(float) * inputNSamples); } for (sectIdx = 0; sectIdx < nSect; sectIdx++) { if (sectIdx == 0) { src = stHdl->inputTempBuf; } else { src = stHdl->sectOutputBuf[sectIdx - 1]; } tgt = stHdl->sectOutputBuf[sectIdx]; for (smplIdx = 0; smplIdx < inputNSamples; smplIdx++) { tmp1 = src[smplIdx] - stHdl->ACoeff[sectIdx][1] * stHdl->sectW[sectIdx][0] - stHdl->ACoeff[sectIdx][2] * stHdl->sectW[sectIdx][1]; tgt[smplIdx] = stHdl->GCoeff[sectIdx] * (stHdl->BCoeff[sectIdx][0] * tmp1 + stHdl->BCoeff[sectIdx][1] * stHdl->sectW[sectIdx][0] + stHdl->BCoeff[sectIdx][2] * stHdl->sectW[sectIdx][1]); stHdl->sectW[sectIdx][1] = stHdl->sectW[sectIdx][0]; stHdl->sectW[sectIdx][0] = tmp1; } } // prepare output buffer if (isFloatIO == 0) { for (smplIdx = 0; smplIdx < inputNSamples; smplIdx++) { ((short*)pOut->outputBuff)[smplIdx] = (short)_BIQUAD_FLOAT2SHORT(stHdl->sectOutputBuf[nSect - 1][smplIdx]); } } else { memcpy(pOut->outputBuff, stHdl->sectOutputBuf[nSect - 1], sizeof(float) * inputNSamples); } return 0; }