/** * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /*------------------------------------------------------------------------------ * * This file includes the coefficient tables or the two convolution function * It also includes the definition of Qmf_storage and the prototype of all * necessary functions required to implement the QMF filtering. * *----------------------------------------------------------------------------*/ #ifndef QMF_H #define QMF_H #include "AptxParameters.h" typedef struct { int32_t QmfL_buf[32]; int32_t QmfH_buf[32]; int32_t QmfLH_buf[32]; int32_t QmfHL_buf[32]; int32_t QmfLL_buf[32]; int32_t QmfHH_buf[32]; int32_t QmfI_pt; int32_t QmfO_pt; } Qmf_storage; /* Outer QMF filter for aptX HD is a symmetrical 32-tap filter (16 * different coefficients). The table defined in QmfConv.c */ #ifndef _STDQMFOUTERCOEFF static const int32_t Qmf_outerCoeffs[12] = { /* (C(1/30)C(3/28)), C(5/26), C(7/24) */ 0xFE6302DA, 0xFFFFDA75, 0x0000AA6A, /* C(9/22), C(11/20), C(13/18), C(15/16) */ 0xFFFE273E, 0x00041E95, 0xFFF710B5, 0x002AC12E, /* C(17/14), C(19/12), (C(21/10)C(23/8)) */ 0x000AA328, 0xFFFD8D1F, 0x211E6BDB, /* (C(25/6)C(27/4)), (C(29/2)C(31/0)) */ 0x0DB7D8C5, 0xFC7F02B0, }; #else static const int32_t Qmf_outerCoeffs[16] = { 730, -413, -9611, 43626, -121026, 269973, -585547, 2801966, 697128, -160481, 27611, 8478, -10043, 3511, 688, -897, }; #endif /* Each inner QMF filter for aptX HD is a symmetrical 32-tap filter (16 * different coefficients) */ static const int32_t Qmf_innerCoeffs[16] = { 1033, -584, -13592, 61697, -171156, 381799, -828088, 3962579, 985888, -226954, 39048, 11990, -14203, 4966, 973, -1268, }; void AsmQmfConvI_HD(const int32_t* p1dl_buffPtr, const int32_t* p2dl_buffPtr, const int32_t* coeffPtr, int32_t* filterOutputs); void AsmQmfConvO_HD(const int32_t* p1dl_buffPtr, const int32_t* p2dl_buffPtr, const int32_t* coeffPtr, int32_t* convSumDiff); XBT_INLINE_ void QmfAnalysisFilter(const int32_t pcm[4], Qmf_storage* Qmf_St, const int32_t* predVals, int32_t* aqmfOutputs) { int32_t convSumDiff[4]; int32_t filterOutputs[4]; int32_t lc_QmfO_pt = (Qmf_St->QmfO_pt); int32_t lc_QmfI_pt = (Qmf_St->QmfI_pt); /* Run the analysis QMF */ /* Symbolic constants to represent the first and second set out outer filter * outputs. */ enum { FirstOuterOutputs = 0, SecondOuterOutputs = 1 }; /* Load outer filter phase1 and phase2 delay lines with the first 2 PCM * samples. Convolve the filter and get the 2 convolution results. */ Qmf_St->QmfL_buf[lc_QmfO_pt + 16] = pcm[FirstPcm]; Qmf_St->QmfL_buf[lc_QmfO_pt] = pcm[FirstPcm]; Qmf_St->QmfH_buf[lc_QmfO_pt + 16] = pcm[SecondPcm]; Qmf_St->QmfH_buf[lc_QmfO_pt++] = pcm[SecondPcm]; lc_QmfO_pt &= 0xF; AsmQmfConvO_HD(&Qmf_St->QmfL_buf[lc_QmfO_pt + 15], &Qmf_St->QmfH_buf[lc_QmfO_pt], Qmf_outerCoeffs, &convSumDiff[0]); /* Load outer filter phase1 and phase2 delay lines with the second 2 PCM * samples. Convolve the filter and get the 2 convolution results. */ Qmf_St->QmfL_buf[lc_QmfO_pt + 16] = pcm[ThirdPcm]; Qmf_St->QmfL_buf[lc_QmfO_pt] = pcm[ThirdPcm]; Qmf_St->QmfH_buf[lc_QmfO_pt + 16] = pcm[FourthPcm]; Qmf_St->QmfH_buf[lc_QmfO_pt++] = pcm[FourthPcm]; lc_QmfO_pt &= 0xF; AsmQmfConvO_HD(&Qmf_St->QmfL_buf[lc_QmfO_pt + 15], &Qmf_St->QmfH_buf[lc_QmfO_pt], Qmf_outerCoeffs, &convSumDiff[1]); /* Load the first inner filter phase1 and phase2 delay lines with the 2 * convolution sum (low-pass) outer filter outputs. Convolve the filter and * get the 2 convolution results. The first 2 analysis filter outputs are * the sum and difference values for the first inner filter convolutions. */ Qmf_St->QmfLL_buf[lc_QmfI_pt + 16] = convSumDiff[0]; Qmf_St->QmfLL_buf[lc_QmfI_pt] = convSumDiff[0]; Qmf_St->QmfLH_buf[lc_QmfI_pt + 16] = convSumDiff[1]; Qmf_St->QmfLH_buf[lc_QmfI_pt] = convSumDiff[1]; AsmQmfConvI_HD(&Qmf_St->QmfLL_buf[lc_QmfI_pt + 16], &Qmf_St->QmfLH_buf[lc_QmfI_pt + 1], &Qmf_innerCoeffs[0], &filterOutputs[LL]); /* Load the second inner filter phase1 and phase2 delay lines with the 2 * convolution difference (high-pass) outer filter outputs. Convolve the * filter and get the 2 convolution results. The second 2 analysis filter * outputs are the sum and difference values for the second inner filter * convolutions. */ Qmf_St->QmfHL_buf[lc_QmfI_pt + 16] = convSumDiff[2]; Qmf_St->QmfHL_buf[lc_QmfI_pt] = convSumDiff[2]; Qmf_St->QmfHH_buf[lc_QmfI_pt + 16] = convSumDiff[3]; Qmf_St->QmfHH_buf[lc_QmfI_pt++] = convSumDiff[3]; lc_QmfI_pt &= 0xF; AsmQmfConvI_HD(&Qmf_St->QmfHL_buf[lc_QmfI_pt + 15], &Qmf_St->QmfHH_buf[lc_QmfI_pt], &Qmf_innerCoeffs[0], &filterOutputs[HL]); /* Subtracted the previous predicted value from the filter output on a * per-subband basis. Ensure these values are saturated, if necessary. * Manual unrolling */ aqmfOutputs[LL] = filterOutputs[LL] - predVals[LL]; if (aqmfOutputs[LL] > 8388607) { aqmfOutputs[LL] = 8388607; } if (aqmfOutputs[LL] < -8388608) { aqmfOutputs[LL] = -8388608; } aqmfOutputs[LH] = filterOutputs[LH] - predVals[LH]; if (aqmfOutputs[LH] > 8388607) { aqmfOutputs[LH] = 8388607; } if (aqmfOutputs[LH] < -8388608) { aqmfOutputs[LH] = -8388608; } aqmfOutputs[HL] = filterOutputs[HL] - predVals[HL]; if (aqmfOutputs[HL] > 8388607) { aqmfOutputs[HL] = 8388607; } if (aqmfOutputs[HL] < -8388608) { aqmfOutputs[HL] = -8388608; } aqmfOutputs[HH] = filterOutputs[HH] - predVals[HH]; if (aqmfOutputs[HH] > 8388607) { aqmfOutputs[HH] = 8388607; } if (aqmfOutputs[HH] < -8388608) { aqmfOutputs[HH] = -8388608; } (Qmf_St->QmfO_pt) = lc_QmfO_pt; (Qmf_St->QmfI_pt) = lc_QmfI_pt; } #endif // QMF_H