aboutsummaryrefslogtreecommitdiffstats
path: root/fdk-aac/libAACdec/src/stereo.cpp
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2019-11-11 11:38:02 +0100
committerMatthias P. Braendli <matthias.braendli@mpb.li>2019-11-11 11:38:02 +0100
commit0e5af65c467b2423a0b857ae3ad98c91acc1e190 (patch)
treed07f69550d8886271e44fe79c4dcfb299cafbd38 /fdk-aac/libAACdec/src/stereo.cpp
parentefe406d9724f959c8bc2a31802559ca6d41fd897 (diff)
downloadODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.tar.gz
ODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.tar.bz2
ODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.zip
Include patched FDK-AAC in the repository
The initial idea was to get the DAB+ patch into upstream, but since that follows the android source releases, there is no place for a custom DAB+ patch there. So instead of having to maintain a patched fdk-aac that has to have the same .so version as the distribution package on which it is installed, we prefer having a separate fdk-aac-dab library to avoid collision. At that point, there's no reason to keep fdk-aac in a separate repository, as odr-audioenc is the only tool that needs DAB+ encoding support. Including it here simplifies installation, and makes it consistent with toolame-dab, also shipped in this repository. DAB+ decoding support (needed by ODR-SourceCompanion, dablin, etisnoop, welle.io and others) can be done using upstream FDK-AAC.
Diffstat (limited to 'fdk-aac/libAACdec/src/stereo.cpp')
-rw-r--r--fdk-aac/libAACdec/src/stereo.cpp1250
1 files changed, 1250 insertions, 0 deletions
diff --git a/fdk-aac/libAACdec/src/stereo.cpp b/fdk-aac/libAACdec/src/stereo.cpp
new file mode 100644
index 0000000..eed826b
--- /dev/null
+++ b/fdk-aac/libAACdec/src/stereo.cpp
@@ -0,0 +1,1250 @@
+/* -----------------------------------------------------------------------------
+Software License for The Fraunhofer FDK AAC Codec Library for Android
+
+© Copyright 1995 - 2018 Fraunhofer-Gesellschaft zur Förderung der angewandten
+Forschung e.V. All rights reserved.
+
+ 1. INTRODUCTION
+The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software
+that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding
+scheme for digital audio. This FDK AAC Codec software is intended to be used on
+a wide variety of Android devices.
+
+AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient
+general perceptual audio codecs. AAC-ELD is considered the best-performing
+full-bandwidth communications codec by independent studies and is widely
+deployed. AAC has been standardized by ISO and IEC as part of the MPEG
+specifications.
+
+Patent licenses for necessary patent claims for the FDK AAC Codec (including
+those of Fraunhofer) may be obtained through Via Licensing
+(www.vialicensing.com) or through the respective patent owners individually for
+the purpose of encoding or decoding bit streams in products that are compliant
+with the ISO/IEC MPEG audio standards. Please note that most manufacturers of
+Android devices already license these patent claims through Via Licensing or
+directly from the patent owners, and therefore FDK AAC Codec software may
+already be covered under those patent licenses when it is used for those
+licensed purposes only.
+
+Commercially-licensed AAC software libraries, including floating-point versions
+with enhanced sound quality, are also available from Fraunhofer. Users are
+encouraged to check the Fraunhofer website for additional applications
+information and documentation.
+
+2. COPYRIGHT LICENSE
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted without payment of copyright license fees provided that you
+satisfy the following conditions:
+
+You must retain the complete text of this software license in redistributions of
+the FDK AAC Codec or your modifications thereto in source code form.
+
+You must retain the complete text of this software license in the documentation
+and/or other materials provided with redistributions of the FDK AAC Codec or
+your modifications thereto in binary form. You must make available free of
+charge copies of the complete source code of the FDK AAC Codec and your
+modifications thereto to recipients of copies in binary form.
+
+The name of Fraunhofer may not be used to endorse or promote products derived
+from this library without prior written permission.
+
+You may not charge copyright license fees for anyone to use, copy or distribute
+the FDK AAC Codec software or your modifications thereto.
+
+Your modified versions of the FDK AAC Codec must carry prominent notices stating
+that you changed the software and the date of any change. For modified versions
+of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android"
+must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK
+AAC Codec Library for Android."
+
+3. NO PATENT LICENSE
+
+NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without
+limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE.
+Fraunhofer provides no warranty of patent non-infringement with respect to this
+software.
+
+You may use this FDK AAC Codec software or modifications thereto only for
+purposes that are authorized by appropriate patent licenses.
+
+4. DISCLAIMER
+
+This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright
+holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+including but not limited to the implied warranties of merchantability and
+fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary,
+or consequential damages, including but not limited to procurement of substitute
+goods or services; loss of use, data, or profits, or business interruption,
+however caused and on any theory of liability, whether in contract, strict
+liability, or tort (including negligence), arising in any way out of the use of
+this software, even if advised of the possibility of such damage.
+
+5. CONTACT INFORMATION
+
+Fraunhofer Institute for Integrated Circuits IIS
+Attention: Audio and Multimedia Departments - FDK AAC LL
+Am Wolfsmantel 33
+91058 Erlangen, Germany
+
+www.iis.fraunhofer.de/amm
+amm-info@iis.fraunhofer.de
+----------------------------------------------------------------------------- */
+
+/**************************** AAC decoder library ******************************
+
+ Author(s): Josef Hoepfl
+
+ Description: joint stereo processing
+
+*******************************************************************************/
+
+#include "stereo.h"
+
+#include "aac_rom.h"
+#include "FDK_bitstream.h"
+#include "channelinfo.h"
+#include "FDK_audio.h"
+
+enum { L = 0, R = 1 };
+
+#include "block.h"
+
+int CJointStereo_Read(HANDLE_FDK_BITSTREAM bs,
+ CJointStereoData *pJointStereoData,
+ const int windowGroups,
+ const int scaleFactorBandsTransmitted,
+ const int max_sfb_ste_clear,
+ CJointStereoPersistentData *pJointStereoPersistentData,
+ CCplxPredictionData *cplxPredictionData,
+ int cplxPredictionActiv, int scaleFactorBandsTotal,
+ int windowSequence, const UINT flags) {
+ int group, band;
+
+ pJointStereoData->MsMaskPresent = (UCHAR)FDKreadBits(bs, 2);
+
+ FDKmemclear(pJointStereoData->MsUsed,
+ scaleFactorBandsTransmitted * sizeof(UCHAR));
+
+ pJointStereoData->cplx_pred_flag = 0;
+ if (cplxPredictionActiv) {
+ cplxPredictionData->pred_dir = 0;
+ cplxPredictionData->complex_coef = 0;
+ cplxPredictionData->use_prev_frame = 0;
+ cplxPredictionData->igf_pred_dir = 0;
+ }
+
+ switch (pJointStereoData->MsMaskPresent) {
+ case 0: /* no M/S */
+ /* all flags are already cleared */
+ break;
+
+ case 1: /* read ms_used */
+ for (group = 0; group < windowGroups; group++) {
+ for (band = 0; band < scaleFactorBandsTransmitted; band++) {
+ pJointStereoData->MsUsed[band] |= (FDKreadBits(bs, 1) << group);
+ }
+ }
+ break;
+
+ case 2: /* full spectrum M/S */
+ for (band = 0; band < scaleFactorBandsTransmitted; band++) {
+ pJointStereoData->MsUsed[band] = 255; /* set all flags to 1 */
+ }
+ break;
+
+ case 3:
+ /* M/S coding is disabled, complex stereo prediction is enabled */
+ if (flags & (AC_USAC | AC_RSVD50 | AC_RSV603DA)) {
+ if (cplxPredictionActiv) { /* 'if (stereoConfigIndex == 0)' */
+
+ pJointStereoData->cplx_pred_flag = 1;
+
+ /* cplx_pred_data() cp. ISO/IEC FDIS 23003-3:2011(E) Table 26 */
+ int cplx_pred_all = 0; /* local use only */
+ cplx_pred_all = FDKreadBits(bs, 1);
+
+ if (cplx_pred_all) {
+ for (group = 0; group < windowGroups; group++) {
+ UCHAR groupmask = ((UCHAR)1 << group);
+ for (band = 0; band < scaleFactorBandsTransmitted; band++) {
+ pJointStereoData->MsUsed[band] |= groupmask;
+ }
+ }
+ } else {
+ for (group = 0; group < windowGroups; group++) {
+ for (band = 0; band < scaleFactorBandsTransmitted;
+ band += SFB_PER_PRED_BAND) {
+ pJointStereoData->MsUsed[band] |= (FDKreadBits(bs, 1) << group);
+ if ((band + 1) < scaleFactorBandsTotal) {
+ pJointStereoData->MsUsed[band + 1] |=
+ (pJointStereoData->MsUsed[band] & ((UCHAR)1 << group));
+ }
+ }
+ }
+ }
+ } else {
+ return -1;
+ }
+ }
+ break;
+ }
+
+ if (cplxPredictionActiv) {
+ /* If all sfb are MS-ed then no complex prediction */
+ if (pJointStereoData->MsMaskPresent == 3) {
+ if (pJointStereoData->cplx_pred_flag) {
+ int delta_code_time = 0;
+
+ /* set pointer to Huffman codebooks */
+ const CodeBookDescription *hcb = &AACcodeBookDescriptionTable[BOOKSCL];
+ /* set predictors to zero in case of a transition from long to short
+ * window sequences and vice versa */
+ if (((windowSequence == BLOCK_SHORT) &&
+ (pJointStereoPersistentData->winSeqPrev != BLOCK_SHORT)) ||
+ ((pJointStereoPersistentData->winSeqPrev == BLOCK_SHORT) &&
+ (windowSequence != BLOCK_SHORT))) {
+ FDKmemclear(pJointStereoPersistentData->alpha_q_re_prev,
+ JointStereoMaximumGroups * JointStereoMaximumBands *
+ sizeof(SHORT));
+ FDKmemclear(pJointStereoPersistentData->alpha_q_im_prev,
+ JointStereoMaximumGroups * JointStereoMaximumBands *
+ sizeof(SHORT));
+ }
+ {
+ FDKmemclear(cplxPredictionData->alpha_q_re,
+ JointStereoMaximumGroups * JointStereoMaximumBands *
+ sizeof(SHORT));
+ FDKmemclear(cplxPredictionData->alpha_q_im,
+ JointStereoMaximumGroups * JointStereoMaximumBands *
+ sizeof(SHORT));
+ }
+
+ /* 0 = mid->side prediction, 1 = side->mid prediction */
+ cplxPredictionData->pred_dir = FDKreadBits(bs, 1);
+ cplxPredictionData->complex_coef = FDKreadBits(bs, 1);
+
+ if (cplxPredictionData->complex_coef) {
+ if (flags & AC_INDEP) {
+ cplxPredictionData->use_prev_frame = 0;
+ } else {
+ cplxPredictionData->use_prev_frame = FDKreadBits(bs, 1);
+ }
+ }
+
+ if (flags & AC_INDEP) {
+ delta_code_time = 0;
+ } else {
+ delta_code_time = FDKreadBits(bs, 1);
+ }
+
+ {
+ int last_alpha_q_re = 0, last_alpha_q_im = 0;
+
+ for (group = 0; group < windowGroups; group++) {
+ for (band = 0; band < scaleFactorBandsTransmitted;
+ band += SFB_PER_PRED_BAND) {
+ if (delta_code_time == 1) {
+ if (group > 0) {
+ last_alpha_q_re =
+ cplxPredictionData->alpha_q_re[group - 1][band];
+ last_alpha_q_im =
+ cplxPredictionData->alpha_q_im[group - 1][band];
+ } else if ((windowSequence == BLOCK_SHORT) &&
+ (pJointStereoPersistentData->winSeqPrev ==
+ BLOCK_SHORT)) {
+ /* Included for error-robustness */
+ if (pJointStereoPersistentData->winGroupsPrev == 0) return -1;
+
+ last_alpha_q_re =
+ pJointStereoPersistentData->alpha_q_re_prev
+ [pJointStereoPersistentData->winGroupsPrev - 1][band];
+ last_alpha_q_im =
+ pJointStereoPersistentData->alpha_q_im_prev
+ [pJointStereoPersistentData->winGroupsPrev - 1][band];
+ } else {
+ last_alpha_q_re =
+ pJointStereoPersistentData->alpha_q_re_prev[group][band];
+ last_alpha_q_im =
+ pJointStereoPersistentData->alpha_q_im_prev[group][band];
+ }
+
+ } else {
+ if (band > 0) {
+ last_alpha_q_re =
+ cplxPredictionData->alpha_q_re[group][band - 1];
+ last_alpha_q_im =
+ cplxPredictionData->alpha_q_im[group][band - 1];
+ } else {
+ last_alpha_q_re = 0;
+ last_alpha_q_im = 0;
+ }
+
+ } /* if (delta_code_time == 1) */
+
+ if (pJointStereoData->MsUsed[band] & ((UCHAR)1 << group)) {
+ int dpcm_alpha_re, dpcm_alpha_im;
+
+ dpcm_alpha_re = CBlock_DecodeHuffmanWord(bs, hcb);
+ dpcm_alpha_re -= 60;
+ dpcm_alpha_re *= -1;
+
+ cplxPredictionData->alpha_q_re[group][band] =
+ dpcm_alpha_re + last_alpha_q_re;
+
+ if (cplxPredictionData->complex_coef) {
+ dpcm_alpha_im = CBlock_DecodeHuffmanWord(bs, hcb);
+ dpcm_alpha_im -= 60;
+ dpcm_alpha_im *= -1;
+
+ cplxPredictionData->alpha_q_im[group][band] =
+ dpcm_alpha_im + last_alpha_q_im;
+ } else {
+ cplxPredictionData->alpha_q_im[group][band] = 0;
+ }
+
+ } else {
+ cplxPredictionData->alpha_q_re[group][band] = 0;
+ cplxPredictionData->alpha_q_im[group][band] = 0;
+ } /* if (pJointStereoData->MsUsed[band] & ((UCHAR)1 << group)) */
+
+ if ((band + 1) <
+ scaleFactorBandsTransmitted) { /* <= this should be the
+ correct way (cp.
+ ISO_IEC_FDIS_23003-0(E) */
+ /* 7.7.2.3.2 Decoding of prediction coefficients) */
+ cplxPredictionData->alpha_q_re[group][band + 1] =
+ cplxPredictionData->alpha_q_re[group][band];
+ cplxPredictionData->alpha_q_im[group][band + 1] =
+ cplxPredictionData->alpha_q_im[group][band];
+ } /* if ((band+1)<scaleFactorBandsTotal) */
+
+ pJointStereoPersistentData->alpha_q_re_prev[group][band] =
+ cplxPredictionData->alpha_q_re[group][band];
+ pJointStereoPersistentData->alpha_q_im_prev[group][band] =
+ cplxPredictionData->alpha_q_im[group][band];
+ }
+
+ for (band = scaleFactorBandsTransmitted; band < max_sfb_ste_clear;
+ band++) {
+ cplxPredictionData->alpha_q_re[group][band] = 0;
+ cplxPredictionData->alpha_q_im[group][band] = 0;
+ pJointStereoPersistentData->alpha_q_re_prev[group][band] = 0;
+ pJointStereoPersistentData->alpha_q_im_prev[group][band] = 0;
+ }
+ }
+ }
+ }
+ } else {
+ for (group = 0; group < windowGroups; group++) {
+ for (band = 0; band < max_sfb_ste_clear; band++) {
+ pJointStereoPersistentData->alpha_q_re_prev[group][band] = 0;
+ pJointStereoPersistentData->alpha_q_im_prev[group][band] = 0;
+ }
+ }
+ }
+
+ pJointStereoPersistentData->winGroupsPrev = windowGroups;
+ }
+
+ return 0;
+}
+
+static void CJointStereo_filterAndAdd(
+ FIXP_DBL *in, int len, int windowLen, const FIXP_FILT *coeff, FIXP_DBL *out,
+ UCHAR isCurrent /* output values with even index get a
+ positve addon (=1) or a negative addon
+ (=0) */
+) {
+ int i, j;
+
+ int indices_1[] = {2, 1, 0, 1, 2, 3};
+ int indices_2[] = {1, 0, 0, 2, 3, 4};
+ int indices_3[] = {0, 0, 1, 3, 4, 5};
+
+ int subtr_1[] = {6, 5, 4, 2, 1, 1};
+ int subtr_2[] = {5, 4, 3, 1, 1, 2};
+ int subtr_3[] = {4, 3, 2, 1, 2, 3};
+
+ if (isCurrent == 1) {
+ /* exploit the symmetry of the table: coeff[6] = - coeff[0],
+ coeff[5] = - coeff[1],
+ coeff[4] = - coeff[2],
+ coeff[3] = 0
+ */
+
+ for (i = 0; i < 3; i++) {
+ out[0] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_1[i]]) >> SR_FNA_OUT;
+ out[0] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[indices_1[5 - i]]) >> SR_FNA_OUT;
+ }
+
+ for (i = 0; i < 3; i++) {
+ out[1] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_2[i]]) >> SR_FNA_OUT;
+ out[1] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[indices_2[5 - i]]) >> SR_FNA_OUT;
+ }
+
+ for (i = 0; i < 3; i++) {
+ out[2] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_3[i]]) >> SR_FNA_OUT;
+ out[2] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[indices_3[5 - i]]) >> SR_FNA_OUT;
+ }
+
+ for (j = 3; j < (len - 3); j++) {
+ for (i = 0; i < 3; i++) {
+ out[j] -= (FIXP_DBL)fMultDiv2(coeff[i], in[j - 3 + i]) >> SR_FNA_OUT;
+ out[j] += (FIXP_DBL)fMultDiv2(coeff[i], in[j + 3 - i]) >> SR_FNA_OUT;
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ out[len - 3] -=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_1[i]]) >> SR_FNA_OUT;
+ out[len - 3] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_1[5 - i]]) >> SR_FNA_OUT;
+ }
+
+ for (i = 0; i < 3; i++) {
+ out[len - 2] -=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_2[i]]) >> SR_FNA_OUT;
+ out[len - 2] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_2[5 - i]]) >> SR_FNA_OUT;
+ }
+
+ for (i = 0; i < 3; i++) {
+ out[len - 1] -=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_3[i]]) >> SR_FNA_OUT;
+ out[len - 1] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_3[5 - i]]) >> SR_FNA_OUT;
+ }
+
+ } else {
+ /* exploit the symmetry of the table: coeff[6] = coeff[0],
+ coeff[5] = coeff[1],
+ coeff[4] = coeff[2]
+ */
+
+ for (i = 0; i < 3; i++) {
+ out[0] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_1[i]] >> SR_FNA_OUT);
+ out[0] -=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[indices_1[5 - i]] >> SR_FNA_OUT);
+ }
+ out[0] -= (FIXP_DBL)fMultDiv2(coeff[3], in[0] >> SR_FNA_OUT);
+
+ for (i = 0; i < 3; i++) {
+ out[1] += (FIXP_DBL)fMultDiv2(coeff[i], in[indices_2[i]] >> SR_FNA_OUT);
+ out[1] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[indices_2[5 - i]] >> SR_FNA_OUT);
+ }
+ out[1] += (FIXP_DBL)fMultDiv2(coeff[3], in[1] >> SR_FNA_OUT);
+
+ for (i = 0; i < 3; i++) {
+ out[2] -= (FIXP_DBL)fMultDiv2(coeff[i], in[indices_3[i]] >> SR_FNA_OUT);
+ out[2] -=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[indices_3[5 - i]] >> SR_FNA_OUT);
+ }
+ out[2] -= (FIXP_DBL)fMultDiv2(coeff[3], in[2] >> SR_FNA_OUT);
+
+ for (j = 3; j < (len - 4); j++) {
+ for (i = 0; i < 3; i++) {
+ out[j] += (FIXP_DBL)fMultDiv2(coeff[i], in[j - 3 + i] >> SR_FNA_OUT);
+ out[j] += (FIXP_DBL)fMultDiv2(coeff[i], in[j + 3 - i] >> SR_FNA_OUT);
+ }
+ out[j] += (FIXP_DBL)fMultDiv2(coeff[3], in[j] >> SR_FNA_OUT);
+
+ j++;
+
+ for (i = 0; i < 3; i++) {
+ out[j] -= (FIXP_DBL)fMultDiv2(coeff[i], in[j - 3 + i] >> SR_FNA_OUT);
+ out[j] -= (FIXP_DBL)fMultDiv2(coeff[i], in[j + 3 - i] >> SR_FNA_OUT);
+ }
+ out[j] -= (FIXP_DBL)fMultDiv2(coeff[3], in[j] >> SR_FNA_OUT);
+ }
+
+ for (i = 0; i < 3; i++) {
+ out[len - 3] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_1[i]] >> SR_FNA_OUT);
+ out[len - 3] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_1[5 - i]] >> SR_FNA_OUT);
+ }
+ out[len - 3] += (FIXP_DBL)fMultDiv2(coeff[3], in[len - 3] >> SR_FNA_OUT);
+
+ for (i = 0; i < 3; i++) {
+ out[len - 2] -=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_2[i]] >> SR_FNA_OUT);
+ out[len - 2] -=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_2[5 - i]] >> SR_FNA_OUT);
+ }
+ out[len - 2] -= (FIXP_DBL)fMultDiv2(coeff[3], in[len - 2] >> SR_FNA_OUT);
+
+ for (i = 0; i < 3; i++) {
+ out[len - 1] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_3[i]] >> SR_FNA_OUT);
+ out[len - 1] +=
+ (FIXP_DBL)fMultDiv2(coeff[i], in[len - subtr_3[5 - i]] >> SR_FNA_OUT);
+ }
+ out[len - 1] += (FIXP_DBL)fMultDiv2(coeff[3], in[len - 1] >> SR_FNA_OUT);
+ }
+}
+
+static inline void CJointStereo_GenerateMSOutput(FIXP_DBL *pSpecLCurrBand,
+ FIXP_DBL *pSpecRCurrBand,
+ UINT leftScale,
+ UINT rightScale,
+ UINT nSfbBands) {
+ unsigned int i;
+
+ FIXP_DBL leftCoefficient0;
+ FIXP_DBL leftCoefficient1;
+ FIXP_DBL leftCoefficient2;
+ FIXP_DBL leftCoefficient3;
+
+ FIXP_DBL rightCoefficient0;
+ FIXP_DBL rightCoefficient1;
+ FIXP_DBL rightCoefficient2;
+ FIXP_DBL rightCoefficient3;
+
+ for (i = nSfbBands; i > 0; i -= 4) {
+ leftCoefficient0 = pSpecLCurrBand[i - 4];
+ leftCoefficient1 = pSpecLCurrBand[i - 3];
+ leftCoefficient2 = pSpecLCurrBand[i - 2];
+ leftCoefficient3 = pSpecLCurrBand[i - 1];
+
+ rightCoefficient0 = pSpecRCurrBand[i - 4];
+ rightCoefficient1 = pSpecRCurrBand[i - 3];
+ rightCoefficient2 = pSpecRCurrBand[i - 2];
+ rightCoefficient3 = pSpecRCurrBand[i - 1];
+
+ /* MS output generation */
+ leftCoefficient0 >>= leftScale;
+ leftCoefficient1 >>= leftScale;
+ leftCoefficient2 >>= leftScale;
+ leftCoefficient3 >>= leftScale;
+
+ rightCoefficient0 >>= rightScale;
+ rightCoefficient1 >>= rightScale;
+ rightCoefficient2 >>= rightScale;
+ rightCoefficient3 >>= rightScale;
+
+ pSpecLCurrBand[i - 4] = leftCoefficient0 + rightCoefficient0;
+ pSpecLCurrBand[i - 3] = leftCoefficient1 + rightCoefficient1;
+ pSpecLCurrBand[i - 2] = leftCoefficient2 + rightCoefficient2;
+ pSpecLCurrBand[i - 1] = leftCoefficient3 + rightCoefficient3;
+
+ pSpecRCurrBand[i - 4] = leftCoefficient0 - rightCoefficient0;
+ pSpecRCurrBand[i - 3] = leftCoefficient1 - rightCoefficient1;
+ pSpecRCurrBand[i - 2] = leftCoefficient2 - rightCoefficient2;
+ pSpecRCurrBand[i - 1] = leftCoefficient3 - rightCoefficient3;
+ }
+}
+
+void CJointStereo_ApplyMS(
+ CAacDecoderChannelInfo *pAacDecoderChannelInfo[2],
+ CAacDecoderStaticChannelInfo *pAacDecoderStaticChannelInfo[2],
+ FIXP_DBL *spectrumL, FIXP_DBL *spectrumR, SHORT *SFBleftScale,
+ SHORT *SFBrightScale, SHORT *specScaleL, SHORT *specScaleR,
+ const SHORT *pScaleFactorBandOffsets, const UCHAR *pWindowGroupLength,
+ const int windowGroups, const int max_sfb_ste_outside,
+ const int scaleFactorBandsTransmittedL,
+ const int scaleFactorBandsTransmittedR, FIXP_DBL *store_dmx_re_prev,
+ SHORT *store_dmx_re_prev_e, const int mainband_flag) {
+ int window, group, band;
+ UCHAR groupMask;
+ CJointStereoData *pJointStereoData =
+ &pAacDecoderChannelInfo[L]->pComData->jointStereoData;
+ CCplxPredictionData *cplxPredictionData =
+ pAacDecoderChannelInfo[L]->pComStaticData->cplxPredictionData;
+
+ int max_sfb_ste =
+ fMax(scaleFactorBandsTransmittedL, scaleFactorBandsTransmittedR);
+ int min_sfb_ste =
+ fMin(scaleFactorBandsTransmittedL, scaleFactorBandsTransmittedR);
+ int scaleFactorBandsTransmitted = min_sfb_ste;
+
+ if (pJointStereoData->cplx_pred_flag) {
+ int windowLen, groupwin, frameMaxScale;
+ CJointStereoPersistentData *pJointStereoPersistentData =
+ &pAacDecoderStaticChannelInfo[L]
+ ->pCpeStaticData->jointStereoPersistentData;
+ FIXP_DBL *const staticSpectralCoeffsL =
+ pAacDecoderStaticChannelInfo[L]
+ ->pCpeStaticData->jointStereoPersistentData.spectralCoeffs[L];
+ FIXP_DBL *const staticSpectralCoeffsR =
+ pAacDecoderStaticChannelInfo[L]
+ ->pCpeStaticData->jointStereoPersistentData.spectralCoeffs[R];
+ SHORT *const staticSpecScaleL =
+ pAacDecoderStaticChannelInfo[L]
+ ->pCpeStaticData->jointStereoPersistentData.specScale[L];
+ SHORT *const staticSpecScaleR =
+ pAacDecoderStaticChannelInfo[L]
+ ->pCpeStaticData->jointStereoPersistentData.specScale[R];
+
+ FIXP_DBL *dmx_re =
+ pAacDecoderStaticChannelInfo[L]
+ ->pCpeStaticData->jointStereoPersistentData.scratchBuffer;
+ FIXP_DBL *dmx_re_prev =
+ pAacDecoderStaticChannelInfo[L]
+ ->pCpeStaticData->jointStereoPersistentData.scratchBuffer +
+ 1024;
+
+ /* When MS is applied over the main band this value gets computed. Otherwise
+ * (for the tiles) it uses the assigned value */
+ SHORT dmx_re_prev_e = *store_dmx_re_prev_e;
+
+ const FIXP_FILT *pCoeff;
+ const FIXP_FILT *pCoeffPrev;
+ int coeffPointerOffset;
+
+ int previousShape = (int)pJointStereoPersistentData->winShapePrev;
+ int currentShape = (int)pAacDecoderChannelInfo[L]->icsInfo.WindowShape;
+
+ /* complex stereo prediction */
+
+ /* 0. preparations */
+
+ /* 0.0. get scratch buffer for downmix MDST */
+ C_AALLOC_SCRATCH_START(dmx_im, FIXP_DBL, 1024);
+
+ /* 0.1. window lengths */
+
+ /* get length of short window for current configuration */
+ windowLen =
+ pAacDecoderChannelInfo[L]->granuleLength; /* framelength 768 => 96,
+ framelength 1024 => 128 */
+
+ /* if this is no short-block set length for long-block */
+ if (pAacDecoderChannelInfo[L]->icsInfo.WindowSequence != BLOCK_SHORT) {
+ windowLen *= 8;
+ }
+
+ /* 0.2. set pointer to filter-coefficients for MDST excitation including
+ * previous frame portions */
+ /* cp. ISO/IEC FDIS 23003-3:2011(E) table 125 */
+
+ /* set pointer to default-position */
+ pCoeffPrev = mdst_filt_coef_prev[previousShape];
+
+ if (cplxPredictionData->complex_coef == 1) {
+ switch (pAacDecoderChannelInfo[L]
+ ->icsInfo.WindowSequence) { /* current window sequence */
+ case BLOCK_SHORT:
+ case BLOCK_LONG:
+ pCoeffPrev = mdst_filt_coef_prev[previousShape];
+ break;
+
+ case BLOCK_START:
+ if ((pJointStereoPersistentData->winSeqPrev == BLOCK_SHORT) ||
+ (pJointStereoPersistentData->winSeqPrev == BLOCK_START)) {
+ /* a stop-start-sequence can only follow on an eight-short-sequence
+ * or a start-sequence */
+ pCoeffPrev = mdst_filt_coef_prev[2 + previousShape];
+ } else {
+ pCoeffPrev = mdst_filt_coef_prev[previousShape];
+ }
+ break;
+
+ case BLOCK_STOP:
+ pCoeffPrev = mdst_filt_coef_prev[2 + previousShape];
+ break;
+
+ default:
+ pCoeffPrev = mdst_filt_coef_prev[previousShape];
+ break;
+ }
+ }
+
+ /* 0.3. set pointer to filter-coefficients for MDST excitation */
+
+ /* define offset of pointer to filter-coefficients for MDST exitation
+ * employing only the current frame */
+ if ((previousShape == SHAPE_SINE) && (currentShape == SHAPE_SINE)) {
+ coeffPointerOffset = 0;
+ } else if ((previousShape == SHAPE_SINE) && (currentShape == SHAPE_KBD)) {
+ coeffPointerOffset = 2;
+ } else if ((previousShape == SHAPE_KBD) && (currentShape == SHAPE_KBD)) {
+ coeffPointerOffset = 1;
+ } else /* if ( (previousShape == SHAPE_KBD) && (currentShape == SHAPE_SINE)
+ ) */
+ {
+ coeffPointerOffset = 3;
+ }
+
+ /* set pointer to filter-coefficient table cp. ISO/IEC FDIS 23003-3:2011(E)
+ * table 124 */
+ switch (pAacDecoderChannelInfo[L]
+ ->icsInfo.WindowSequence) { /* current window sequence */
+ case BLOCK_SHORT:
+ case BLOCK_LONG:
+ pCoeff = mdst_filt_coef_curr[coeffPointerOffset];
+ break;
+
+ case BLOCK_START:
+ if ((pJointStereoPersistentData->winSeqPrev == BLOCK_SHORT) ||
+ (pJointStereoPersistentData->winSeqPrev == BLOCK_START)) {
+ /* a stop-start-sequence can only follow on an eight-short-sequence or
+ * a start-sequence */
+ pCoeff = mdst_filt_coef_curr[12 + coeffPointerOffset];
+ } else {
+ pCoeff = mdst_filt_coef_curr[4 + coeffPointerOffset];
+ }
+ break;
+
+ case BLOCK_STOP:
+ pCoeff = mdst_filt_coef_curr[8 + coeffPointerOffset];
+ break;
+
+ default:
+ pCoeff = mdst_filt_coef_curr[coeffPointerOffset];
+ }
+
+ /* 0.4. find maximum common (l/r) band-scaling-factor for whole sequence
+ * (all windows) */
+ frameMaxScale = 0;
+ for (window = 0, group = 0; group < windowGroups; group++) {
+ for (groupwin = 0; groupwin < pWindowGroupLength[group];
+ groupwin++, window++) {
+ SHORT *leftScale = &SFBleftScale[window * 16];
+ SHORT *rightScale = &SFBrightScale[window * 16];
+ int windowMaxScale = 0;
+
+ /* find maximum scaling factor of all bands in this window */
+ for (band = 0; band < min_sfb_ste; band++) {
+ int lScale = leftScale[band];
+ int rScale = rightScale[band];
+ int commonScale = ((lScale > rScale) ? lScale : rScale);
+ windowMaxScale =
+ (windowMaxScale < commonScale) ? commonScale : windowMaxScale;
+ }
+ if (scaleFactorBandsTransmittedL >
+ min_sfb_ste) { /* i.e. scaleFactorBandsTransmittedL == max_sfb_ste
+ */
+ for (; band < max_sfb_ste; band++) {
+ int lScale = leftScale[band];
+ windowMaxScale =
+ (windowMaxScale < lScale) ? lScale : windowMaxScale;
+ }
+ } else {
+ if (scaleFactorBandsTransmittedR >
+ min_sfb_ste) { /* i.e. scaleFactorBandsTransmittedR == max_sfb_ste
+ */
+ for (; band < max_sfb_ste; band++) {
+ int rScale = rightScale[band];
+ windowMaxScale =
+ (windowMaxScale < rScale) ? rScale : windowMaxScale;
+ }
+ }
+ }
+
+ /* find maximum common SF of all windows */
+ frameMaxScale =
+ (frameMaxScale < windowMaxScale) ? windowMaxScale : frameMaxScale;
+ }
+ }
+
+ /* add some headroom for overflow protection during filter and add operation
+ */
+ frameMaxScale += 2;
+
+ /* process on window-basis (i.e. iterate over all groups and corresponding
+ * windows) */
+ for (window = 0, group = 0; group < windowGroups; group++) {
+ groupMask = 1 << group;
+
+ for (groupwin = 0; groupwin < pWindowGroupLength[group];
+ groupwin++, window++) {
+ /* initialize the MDST with zeros */
+ FDKmemclear(&dmx_im[windowLen * window], windowLen * sizeof(FIXP_DBL));
+
+ /* 1. calculate the previous downmix MDCT. We do this once just for the
+ * Main band. */
+ if (cplxPredictionData->complex_coef == 1) {
+ if ((cplxPredictionData->use_prev_frame == 1) && (mainband_flag)) {
+ /* if this is a long-block or the first window of a short-block
+ calculate the downmix MDCT of the previous frame.
+ use_prev_frame is assumed not to change during a frame!
+ */
+
+ /* first determine shiftfactors to scale left and right channel */
+ if ((pAacDecoderChannelInfo[L]->icsInfo.WindowSequence !=
+ BLOCK_SHORT) ||
+ (window == 0)) {
+ int index_offset = 0;
+ int srLeftChan = 0;
+ int srRightChan = 0;
+ if (pAacDecoderChannelInfo[L]->icsInfo.WindowSequence ==
+ BLOCK_SHORT) {
+ /* use the last window of the previous frame for MDCT
+ * calculation if this is a short-block. */
+ index_offset = windowLen * 7;
+ if (staticSpecScaleL[7] > staticSpecScaleR[7]) {
+ srRightChan = staticSpecScaleL[7] - staticSpecScaleR[7];
+ dmx_re_prev_e = staticSpecScaleL[7];
+ } else {
+ srLeftChan = staticSpecScaleR[7] - staticSpecScaleL[7];
+ dmx_re_prev_e = staticSpecScaleR[7];
+ }
+ } else {
+ if (staticSpecScaleL[0] > staticSpecScaleR[0]) {
+ srRightChan = staticSpecScaleL[0] - staticSpecScaleR[0];
+ dmx_re_prev_e = staticSpecScaleL[0];
+ } else {
+ srLeftChan = staticSpecScaleR[0] - staticSpecScaleL[0];
+ dmx_re_prev_e = staticSpecScaleR[0];
+ }
+ }
+
+ /* now scale channels and determine downmix MDCT of previous frame
+ */
+ if (pAacDecoderStaticChannelInfo[L]
+ ->pCpeStaticData->jointStereoPersistentData
+ .clearSpectralCoeffs == 1) {
+ FDKmemclear(dmx_re_prev, windowLen * sizeof(FIXP_DBL));
+ dmx_re_prev_e = 0;
+ } else {
+ if (cplxPredictionData->pred_dir == 0) {
+ for (int i = 0; i < windowLen; i++) {
+ dmx_re_prev[i] =
+ ((staticSpectralCoeffsL[index_offset + i] >>
+ srLeftChan) +
+ (staticSpectralCoeffsR[index_offset + i] >>
+ srRightChan)) >>
+ 1;
+ }
+ } else {
+ for (int i = 0; i < windowLen; i++) {
+ dmx_re_prev[i] =
+ ((staticSpectralCoeffsL[index_offset + i] >>
+ srLeftChan) -
+ (staticSpectralCoeffsR[index_offset + i] >>
+ srRightChan)) >>
+ 1;
+ }
+ }
+ }
+
+ /* In case that we use INF we have to preserve the state of the
+ "dmx_re_prev" (original or computed). This is necessary because we
+ have to apply MS over the separate IGF tiles. */
+ FDKmemcpy(store_dmx_re_prev, &dmx_re_prev[0],
+ windowLen * sizeof(FIXP_DBL));
+
+ /* Particular exponent of the computed/original "dmx_re_prev" must
+ * be kept for the tile MS calculations if necessary.*/
+ *store_dmx_re_prev_e = dmx_re_prev_e;
+
+ } /* if ( (pAacDecoderChannelInfo[L]->icsInfo.WindowSequence !=
+ BLOCK_SHORT) || (window == 0) ) */
+
+ } /* if ( pJointStereoData->use_prev_frame == 1 ) */
+
+ } /* if ( pJointStereoData->complex_coef == 1 ) */
+
+ /* 2. calculate downmix MDCT of current frame */
+
+ /* set pointer to scale-factor-bands of current window */
+ SHORT *leftScale = &SFBleftScale[window * 16];
+ SHORT *rightScale = &SFBrightScale[window * 16];
+
+ specScaleL[window] = specScaleR[window] = frameMaxScale;
+
+ /* adapt scaling-factors to previous frame */
+ if (cplxPredictionData->use_prev_frame == 1) {
+ if (window == 0) {
+ if (dmx_re_prev_e < frameMaxScale) {
+ if (mainband_flag == 0) {
+ scaleValues(dmx_re_prev, store_dmx_re_prev, windowLen,
+ -(frameMaxScale - dmx_re_prev_e));
+ } else {
+ for (int i = 0; i < windowLen; i++) {
+ dmx_re_prev[i] >>= (frameMaxScale - dmx_re_prev_e);
+ }
+ }
+ } else {
+ if (mainband_flag == 0) {
+ FDKmemcpy(dmx_re_prev, store_dmx_re_prev,
+ windowLen * sizeof(FIXP_DBL));
+ }
+ specScaleL[0] = dmx_re_prev_e;
+ specScaleR[0] = dmx_re_prev_e;
+ }
+ } else { /* window != 0 */
+ FDK_ASSERT(pAacDecoderChannelInfo[L]->icsInfo.WindowSequence ==
+ BLOCK_SHORT);
+ if (specScaleL[window - 1] < frameMaxScale) {
+ for (int i = 0; i < windowLen; i++) {
+ dmx_re[windowLen * (window - 1) + i] >>=
+ (frameMaxScale - specScaleL[window - 1]);
+ }
+ } else {
+ specScaleL[window] = specScaleL[window - 1];
+ specScaleR[window] = specScaleR[window - 1];
+ }
+ }
+ } /* if ( pJointStereoData->use_prev_frame == 1 ) */
+
+ /* scaling factors of both channels ought to be equal now */
+ FDK_ASSERT(specScaleL[window] == specScaleR[window]);
+
+ /* rescale signal and calculate downmix MDCT */
+ for (band = 0; band < max_sfb_ste; band++) {
+ /* first adapt scaling of current band to scaling of current window =>
+ * shift signal right */
+ int lScale = leftScale[band];
+ int rScale = rightScale[band];
+
+ lScale = fMin(DFRACT_BITS - 1, specScaleL[window] - lScale);
+ rScale = fMin(DFRACT_BITS - 1,
+ specScaleL[window] - rScale); /* L or R doesn't
+ matter,
+ specScales are
+ equal at this
+ point */
+
+ /* Write back to sfb scale to cover the case when max_sfb_ste <
+ * max_sfb */
+ leftScale[band] = rightScale[band] = specScaleL[window];
+
+ for (int i = pScaleFactorBandOffsets[band];
+ i < pScaleFactorBandOffsets[band + 1]; i++) {
+ spectrumL[windowLen * window + i] >>= lScale;
+ spectrumR[windowLen * window + i] >>= rScale;
+ }
+
+ /* now calculate downmix MDCT */
+ if (pJointStereoData->MsUsed[band] & groupMask) {
+ for (int i = pScaleFactorBandOffsets[band];
+ i < pScaleFactorBandOffsets[band + 1]; i++) {
+ dmx_re[windowLen * window + i] =
+ spectrumL[windowLen * window + i];
+ }
+ } else {
+ if (cplxPredictionData->pred_dir == 0) {
+ for (int i = pScaleFactorBandOffsets[band];
+ i < pScaleFactorBandOffsets[band + 1]; i++) {
+ dmx_re[windowLen * window + i] =
+ (spectrumL[windowLen * window + i] +
+ spectrumR[windowLen * window + i]) >>
+ 1;
+ }
+ } else {
+ for (int i = pScaleFactorBandOffsets[band];
+ i < pScaleFactorBandOffsets[band + 1]; i++) {
+ dmx_re[windowLen * window + i] =
+ (spectrumL[windowLen * window + i] -
+ spectrumR[windowLen * window + i]) >>
+ 1;
+ }
+ }
+ }
+
+ } /* for ( band=0; band<max_sfb_ste; band++ ) */
+ /* Clean until the end */
+ for (int i = pScaleFactorBandOffsets[max_sfb_ste_outside];
+ i < windowLen; i++) {
+ dmx_re[windowLen * window + i] = (FIXP_DBL)0;
+ }
+
+ /* 3. calculate MDST-portion corresponding to the current frame. */
+ if (cplxPredictionData->complex_coef == 1) {
+ {
+ /* 3.1 move pointer in filter-coefficient table in case of short
+ * window sequence */
+ /* (other coefficients are utilized for the last 7 short
+ * windows) */
+ if ((pAacDecoderChannelInfo[L]->icsInfo.WindowSequence ==
+ BLOCK_SHORT) &&
+ (window != 0)) {
+ pCoeff = mdst_filt_coef_curr[currentShape];
+ pCoeffPrev = mdst_filt_coef_prev[currentShape];
+ }
+
+ /* The length of the filter processing must be extended because of
+ * filter boundary problems */
+ int extended_band = fMin(
+ pScaleFactorBandOffsets[max_sfb_ste_outside] + 7, windowLen);
+
+ /* 3.2. estimate downmix MDST from current frame downmix MDCT */
+ if ((pAacDecoderChannelInfo[L]->icsInfo.WindowSequence ==
+ BLOCK_SHORT) &&
+ (window != 0)) {
+ CJointStereo_filterAndAdd(&dmx_re[windowLen * window],
+ extended_band, windowLen, pCoeff,
+ &dmx_im[windowLen * window], 1);
+
+ CJointStereo_filterAndAdd(&dmx_re[windowLen * (window - 1)],
+ extended_band, windowLen, pCoeffPrev,
+ &dmx_im[windowLen * window], 0);
+ } else {
+ CJointStereo_filterAndAdd(dmx_re, extended_band, windowLen,
+ pCoeff, dmx_im, 1);
+
+ if (cplxPredictionData->use_prev_frame == 1) {
+ CJointStereo_filterAndAdd(dmx_re_prev, extended_band, windowLen,
+ pCoeffPrev,
+ &dmx_im[windowLen * window], 0);
+ }
+ }
+
+ } /* if(pAacDecoderChannelInfo[L]->transform_splitting_active) */
+ } /* if ( pJointStereoData->complex_coef == 1 ) */
+
+ /* 4. upmix process */
+ INT pred_dir = cplxPredictionData->pred_dir ? -1 : 1;
+ /* 0.1 in Q-3.34 */
+ const FIXP_DBL pointOne = 0x66666666; /* 0.8 */
+ /* Shift value for the downmix */
+ const INT shift_dmx = SF_FNA_COEFFS + 1;
+
+ for (band = 0; band < max_sfb_ste_outside; band++) {
+ if (pJointStereoData->MsUsed[band] & groupMask) {
+ FIXP_SGL tempRe =
+ (FIXP_SGL)cplxPredictionData->alpha_q_re[group][band];
+ FIXP_SGL tempIm =
+ (FIXP_SGL)cplxPredictionData->alpha_q_im[group][band];
+
+ /* Find the minimum common headroom for alpha_re and alpha_im */
+ int alpha_re_headroom = CountLeadingBits((INT)tempRe) - 16;
+ if (tempRe == (FIXP_SGL)0) alpha_re_headroom = 15;
+ int alpha_im_headroom = CountLeadingBits((INT)tempIm) - 16;
+ if (tempIm == (FIXP_SGL)0) alpha_im_headroom = 15;
+ int val = fMin(alpha_re_headroom, alpha_im_headroom);
+
+ /* Multiply alpha by 0.1 with maximum precision */
+ FDK_ASSERT(val >= 0);
+ FIXP_DBL alpha_re_tmp = fMult((FIXP_SGL)(tempRe << val), pointOne);
+ FIXP_DBL alpha_im_tmp = fMult((FIXP_SGL)(tempIm << val), pointOne);
+
+ /* Calculate alpha exponent */
+ /* (Q-3.34 * Q15.0) shifted left by "val" */
+ int alpha_re_exp = -3 + 15 - val;
+
+ int help3_shift = alpha_re_exp + 1;
+
+ FIXP_DBL *p2CoeffL = &(
+ spectrumL[windowLen * window + pScaleFactorBandOffsets[band]]);
+ FIXP_DBL *p2CoeffR = &(
+ spectrumR[windowLen * window + pScaleFactorBandOffsets[band]]);
+ FIXP_DBL *p2dmxIm =
+ &(dmx_im[windowLen * window + pScaleFactorBandOffsets[band]]);
+ FIXP_DBL *p2dmxRe =
+ &(dmx_re[windowLen * window + pScaleFactorBandOffsets[band]]);
+
+ for (int i = pScaleFactorBandOffsets[band];
+ i < pScaleFactorBandOffsets[band + 1]; i++) {
+ /* Calculating helper term:
+ side = specR[i] - alpha_re[i] * dmx_re[i] - alpha_im[i] *
+ dmx_im[i];
+
+ Here "dmx_re" may be the same as "specL" or alternatively keep
+ the downmix. "dmx_re" and "specL" are two different pointers
+ pointing to separate arrays, which may or may not contain the
+ same data (with different scaling).
+ */
+
+ /* help1: alpha_re[i] * dmx_re[i] */
+ FIXP_DBL help1 = fMultDiv2(alpha_re_tmp, *p2dmxRe++);
+
+ /* tmp: dmx_im[i] */
+ FIXP_DBL tmp = (*p2dmxIm++) << shift_dmx;
+
+ /* help2: alpha_im[i] * dmx_im[i] */
+ FIXP_DBL help2 = fMultDiv2(alpha_im_tmp, tmp);
+
+ /* help3: alpha_re[i] * dmx_re[i] + alpha_im[i] * dmx_im[i] */
+ FIXP_DBL help3 = help1 + help2;
+
+ /* side (= help4) = specR[i] - (dmx_re[i] * specL[i] + alpha_im[i]
+ * * dmx_im[i]) */
+ FIXP_DBL help4 = *p2CoeffR - scaleValue(help3, help3_shift);
+
+ /* We calculate the left and right output by using the helper
+ * function */
+ /* specR[i] = -/+ (specL[i] - side); */
+ *p2CoeffR =
+ (FIXP_DBL)((LONG)(*p2CoeffL - help4) * (LONG)pred_dir);
+ p2CoeffR++;
+
+ /* specL[i] = specL[i] + side; */
+ *p2CoeffL = *p2CoeffL + help4;
+ p2CoeffL++;
+ }
+ }
+
+ } /* for ( band=0; band < max_sfb_ste; band++ ) */
+ } /* for ( groupwin=0; groupwin<pWindowGroupLength[group]; groupwin++,
+ window++ ) */
+
+ } /* for ( window = 0, group = 0; group < windowGroups; group++ ) */
+
+ /* free scratch buffer */
+ C_AALLOC_SCRATCH_END(dmx_im, FIXP_DBL, 1024);
+
+ } else {
+ /* MS stereo */
+
+ for (window = 0, group = 0; group < windowGroups; group++) {
+ groupMask = 1 << group;
+
+ for (int groupwin = 0; groupwin < pWindowGroupLength[group];
+ groupwin++, window++) {
+ FIXP_DBL *leftSpectrum, *rightSpectrum;
+ SHORT *leftScale = &SFBleftScale[window * 16];
+ SHORT *rightScale = &SFBrightScale[window * 16];
+
+ leftSpectrum =
+ SPEC(spectrumL, window, pAacDecoderChannelInfo[L]->granuleLength);
+ rightSpectrum =
+ SPEC(spectrumR, window, pAacDecoderChannelInfo[R]->granuleLength);
+
+ for (band = 0; band < max_sfb_ste_outside; band++) {
+ if (pJointStereoData->MsUsed[band] & groupMask) {
+ int lScale = leftScale[band];
+ int rScale = rightScale[band];
+ int commonScale = lScale > rScale ? lScale : rScale;
+ unsigned int offsetCurrBand, offsetNextBand;
+
+ /* ISO/IEC 14496-3 Chapter 4.6.8.1.1 :
+ M/S joint channel coding can only be used if common_window is 1.
+ */
+ FDK_ASSERT(GetWindowSequence(&pAacDecoderChannelInfo[L]->icsInfo) ==
+ GetWindowSequence(&pAacDecoderChannelInfo[R]->icsInfo));
+ FDK_ASSERT(GetWindowShape(&pAacDecoderChannelInfo[L]->icsInfo) ==
+ GetWindowShape(&pAacDecoderChannelInfo[R]->icsInfo));
+
+ commonScale++;
+ leftScale[band] = commonScale;
+ rightScale[band] = commonScale;
+
+ lScale = fMin(DFRACT_BITS - 1, commonScale - lScale);
+ rScale = fMin(DFRACT_BITS - 1, commonScale - rScale);
+
+ FDK_ASSERT(lScale >= 0 && rScale >= 0);
+
+ offsetCurrBand = pScaleFactorBandOffsets[band];
+ offsetNextBand = pScaleFactorBandOffsets[band + 1];
+
+ CJointStereo_GenerateMSOutput(&(leftSpectrum[offsetCurrBand]),
+ &(rightSpectrum[offsetCurrBand]),
+ lScale, rScale,
+ offsetNextBand - offsetCurrBand);
+ }
+ }
+ if (scaleFactorBandsTransmittedL > scaleFactorBandsTransmitted) {
+ for (; band < scaleFactorBandsTransmittedL; band++) {
+ if (pJointStereoData->MsUsed[band] & groupMask) {
+ rightScale[band] = leftScale[band];
+
+ for (int index = pScaleFactorBandOffsets[band];
+ index < pScaleFactorBandOffsets[band + 1]; index++) {
+ FIXP_DBL leftCoefficient = leftSpectrum[index];
+ /* FIXP_DBL rightCoefficient = (FIXP_DBL)0; */
+ rightSpectrum[index] = leftCoefficient;
+ }
+ }
+ }
+ } else if (scaleFactorBandsTransmittedR > scaleFactorBandsTransmitted) {
+ for (; band < scaleFactorBandsTransmittedR; band++) {
+ if (pJointStereoData->MsUsed[band] & groupMask) {
+ leftScale[band] = rightScale[band];
+
+ for (int index = pScaleFactorBandOffsets[band];
+ index < pScaleFactorBandOffsets[band + 1]; index++) {
+ /* FIXP_DBL leftCoefficient = (FIXP_DBL)0; */
+ FIXP_DBL rightCoefficient = rightSpectrum[index];
+
+ leftSpectrum[index] = rightCoefficient;
+ rightSpectrum[index] = -rightCoefficient;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Reset MsUsed flags if no explicit signalling was transmitted. Necessary
+ for intensity coding. PNS correlation signalling was mapped before
+ calling CJointStereo_ApplyMS(). */
+ if (pJointStereoData->MsMaskPresent == 2) {
+ FDKmemclear(pJointStereoData->MsUsed,
+ JointStereoMaximumBands * sizeof(UCHAR));
+ }
+ }
+}
+
+void CJointStereo_ApplyIS(CAacDecoderChannelInfo *pAacDecoderChannelInfo[2],
+ const SHORT *pScaleFactorBandOffsets,
+ const UCHAR *pWindowGroupLength,
+ const int windowGroups,
+ const int scaleFactorBandsTransmitted) {
+ CJointStereoData *pJointStereoData =
+ &pAacDecoderChannelInfo[L]->pComData->jointStereoData;
+
+ for (int window = 0, group = 0; group < windowGroups; group++) {
+ UCHAR *CodeBook;
+ SHORT *ScaleFactor;
+ UCHAR groupMask = 1 << group;
+
+ CodeBook = &pAacDecoderChannelInfo[R]->pDynData->aCodeBook[group * 16];
+ ScaleFactor =
+ &pAacDecoderChannelInfo[R]->pDynData->aScaleFactor[group * 16];
+
+ for (int groupwin = 0; groupwin < pWindowGroupLength[group];
+ groupwin++, window++) {
+ FIXP_DBL *leftSpectrum, *rightSpectrum;
+ SHORT *leftScale =
+ &pAacDecoderChannelInfo[L]->pDynData->aSfbScale[window * 16];
+ SHORT *rightScale =
+ &pAacDecoderChannelInfo[R]->pDynData->aSfbScale[window * 16];
+ int band;
+
+ leftSpectrum = SPEC(pAacDecoderChannelInfo[L]->pSpectralCoefficient,
+ window, pAacDecoderChannelInfo[L]->granuleLength);
+ rightSpectrum = SPEC(pAacDecoderChannelInfo[R]->pSpectralCoefficient,
+ window, pAacDecoderChannelInfo[R]->granuleLength);
+
+ for (band = 0; band < scaleFactorBandsTransmitted; band++) {
+ if ((CodeBook[band] == INTENSITY_HCB) ||
+ (CodeBook[band] == INTENSITY_HCB2)) {
+ int bandScale = -(ScaleFactor[band] + 100);
+
+ int msb = bandScale >> 2;
+ int lsb = bandScale & 0x03;
+
+ /* exponent of MantissaTable[lsb][0] is 1, thus msb+1 below. */
+ FIXP_DBL scale = MantissaTable[lsb][0];
+
+ /* ISO/IEC 14496-3 Chapter 4.6.8.2.3 :
+ The use of intensity stereo coding is signaled by the use of the
+ pseudo codebooks INTENSITY_HCB and INTENSITY_HCB2 (15 and 14) only
+ in the right channel of a channel_pair_element() having a common
+ ics_info() (common_window == 1). */
+ FDK_ASSERT(GetWindowSequence(&pAacDecoderChannelInfo[L]->icsInfo) ==
+ GetWindowSequence(&pAacDecoderChannelInfo[R]->icsInfo));
+ FDK_ASSERT(GetWindowShape(&pAacDecoderChannelInfo[L]->icsInfo) ==
+ GetWindowShape(&pAacDecoderChannelInfo[R]->icsInfo));
+
+ rightScale[band] = leftScale[band] + msb + 1;
+
+ if (pJointStereoData->MsUsed[band] & groupMask) {
+ if (CodeBook[band] == INTENSITY_HCB) /* _NOT_ in-phase */
+ {
+ scale = -scale;
+ }
+ } else {
+ if (CodeBook[band] == INTENSITY_HCB2) /* out-of-phase */
+ {
+ scale = -scale;
+ }
+ }
+
+ for (int index = pScaleFactorBandOffsets[band];
+ index < pScaleFactorBandOffsets[band + 1]; index++) {
+ rightSpectrum[index] = fMult(leftSpectrum[index], scale);
+ }
+ }
+ }
+ }
+ }
+}