• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -----------------------------------------------------------------------------
2 Software License for The Fraunhofer FDK AAC Codec Library for Android
3 
4 © Copyright  1995 - 2018 Fraunhofer-Gesellschaft zur Förderung der angewandten
5 Forschung e.V. All rights reserved.
6 
7  1.    INTRODUCTION
8 The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software
9 that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding
10 scheme for digital audio. This FDK AAC Codec software is intended to be used on
11 a wide variety of Android devices.
12 
13 AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient
14 general perceptual audio codecs. AAC-ELD is considered the best-performing
15 full-bandwidth communications codec by independent studies and is widely
16 deployed. AAC has been standardized by ISO and IEC as part of the MPEG
17 specifications.
18 
19 Patent licenses for necessary patent claims for the FDK AAC Codec (including
20 those of Fraunhofer) may be obtained through Via Licensing
21 (www.vialicensing.com) or through the respective patent owners individually for
22 the purpose of encoding or decoding bit streams in products that are compliant
23 with the ISO/IEC MPEG audio standards. Please note that most manufacturers of
24 Android devices already license these patent claims through Via Licensing or
25 directly from the patent owners, and therefore FDK AAC Codec software may
26 already be covered under those patent licenses when it is used for those
27 licensed purposes only.
28 
29 Commercially-licensed AAC software libraries, including floating-point versions
30 with enhanced sound quality, are also available from Fraunhofer. Users are
31 encouraged to check the Fraunhofer website for additional applications
32 information and documentation.
33 
34 2.    COPYRIGHT LICENSE
35 
36 Redistribution and use in source and binary forms, with or without modification,
37 are permitted without payment of copyright license fees provided that you
38 satisfy the following conditions:
39 
40 You must retain the complete text of this software license in redistributions of
41 the FDK AAC Codec or your modifications thereto in source code form.
42 
43 You must retain the complete text of this software license in the documentation
44 and/or other materials provided with redistributions of the FDK AAC Codec or
45 your modifications thereto in binary form. You must make available free of
46 charge copies of the complete source code of the FDK AAC Codec and your
47 modifications thereto to recipients of copies in binary form.
48 
49 The name of Fraunhofer may not be used to endorse or promote products derived
50 from this library without prior written permission.
51 
52 You may not charge copyright license fees for anyone to use, copy or distribute
53 the FDK AAC Codec software or your modifications thereto.
54 
55 Your modified versions of the FDK AAC Codec must carry prominent notices stating
56 that you changed the software and the date of any change. For modified versions
57 of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android"
58 must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK
59 AAC Codec Library for Android."
60 
61 3.    NO PATENT LICENSE
62 
63 NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without
64 limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE.
65 Fraunhofer provides no warranty of patent non-infringement with respect to this
66 software.
67 
68 You may use this FDK AAC Codec software or modifications thereto only for
69 purposes that are authorized by appropriate patent licenses.
70 
71 4.    DISCLAIMER
72 
73 This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright
74 holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
75 including but not limited to the implied warranties of merchantability and
76 fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
77 CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary,
78 or consequential damages, including but not limited to procurement of substitute
79 goods or services; loss of use, data, or profits, or business interruption,
80 however caused and on any theory of liability, whether in contract, strict
81 liability, or tort (including negligence), arising in any way out of the use of
82 this software, even if advised of the possibility of such damage.
83 
84 5.    CONTACT INFORMATION
85 
86 Fraunhofer Institute for Integrated Circuits IIS
87 Attention: Audio and Multimedia Departments - FDK AAC LL
88 Am Wolfsmantel 33
89 91058 Erlangen, Germany
90 
91 www.iis.fraunhofer.de/amm
92 amm-info@iis.fraunhofer.de
93 ----------------------------------------------------------------------------- */
94 
95 /*********************** MPEG surround encoder library *************************
96 
97    Author(s):   Max Neuendorf
98 
99    Description: Encoder Library Interface
100                 Interface to Spacial Audio Coding Encoder lib
101 
102 *******************************************************************************/
103 
104 /****************************************************************************
105 \file
106 Description of file contents
107 ******************************************************************************/
108 
109 /* Includes ******************************************************************/
110 #include "sacenc_lib.h"
111 #include "sacenc_const.h"
112 #include "genericStds.h"
113 #include "FDK_core.h"
114 #include "sacenc_tree.h"
115 #include "sacenc_bitstream.h"
116 #include "sacenc_onsetdetect.h"
117 #include "sacenc_framewindowing.h"
118 #include "sacenc_filter.h"
119 #include "sacenc_paramextract.h"
120 #include "sacenc_staticgain.h"
121 #include "sacenc_delay.h"
122 #include "sacenc_dmx_tdom_enh.h"
123 #include "sacenc_vectorfunctions.h"
124 #include "qmf.h"
125 
126 /* Defines *******************************************************************/
127 
128 /* Encoder library info */
129 #define SACENC_LIB_VL0 2
130 #define SACENC_LIB_VL1 0
131 #define SACENC_LIB_VL2 0
132 #define SACENC_LIB_TITLE "MPEG Surround Encoder"
133 #ifdef __ANDROID__
134 #define SACENC_LIB_BUILD_DATE ""
135 #define SACENC_LIB_BUILD_TIME ""
136 #else
137 #define SACENC_LIB_BUILD_DATE __DATE__
138 #define SACENC_LIB_BUILD_TIME __TIME__
139 #endif
140 
141 #define MAX_MPEGS_BYTES (1 << 14)
142 #define MAX_SSC_BYTES (1 << 6)
143 
144 #define MAX_SPACE_TREE_CHANNELS 2
145 #define NUM_KEEP_WINDOWS 3
146 
147 /* Data Types ****************************************************************/
148 typedef struct {
149   MP4SPACEENC_MODE encMode;
150   MP4SPACEENC_BANDS_CONFIG nParamBands;
151   MP4SPACEENC_QUANTMODE quantMode;
152   UCHAR bUseCoarseQuant;
153   UCHAR bLdMode;
154   UCHAR bTimeDomainDmx;
155   UINT sampleRate;
156   UINT frameTimeSlots;     /* e.g. 32 when used with HE-AAC */
157   UINT independencyFactor; /* how often should we set the independency flag */
158   INT timeAlignment;       /* additional delay for downmix */
159 
160 } MP4SPACEENC_SETUP, *HANDLE_MP4SPACEENC_SETUP;
161 
162 struct ENC_CONFIG_SETUP {
163   UCHAR bEncMode_212;
164   UCHAR maxHybridInStaticSlots;
165   LONG maxSamplingrate;
166   INT maxAnalysisLengthTimeSlots;
167   INT maxHybridBands;
168   INT maxQmfBands;
169   INT maxChIn;
170   INT maxFrameTimeSlots;
171   INT maxFrameLength;
172   INT maxChOut;
173   INT maxChTotOut;
174 };
175 
176 struct MP4SPACE_ENCODER {
177   MP4SPACEENC_SETUP user;
178 
179   ENC_CONFIG_SETUP setup; /* describe allocated instance */
180 
181   HANDLE_FRAMEWINDOW
182   hFrameWindow;      /* Windowing, only created+updated, but not used */
183   INT nSamplesValid; /* Input Buffer Handling */
184 
185   /* Routing Sensible Switches/Variables */
186   MP4SPACEENC_BANDS_CONFIG nParamBands;
187   UCHAR useTimeDomDownmix;
188 
189   /* not Routing Sensible Switches/Varibles - must be contained in Check */
190   MP4SPACEENC_MODE encMode;
191   UCHAR bEncMode_212_only;
192 
193   /* not Routing Sensible Switches/Varibles + lower Classes */
194   UCHAR useFrameKeep;
195   UINT independencyFactor;
196   UINT nSampleRate;
197   UCHAR nInputChannels;
198   UCHAR nOutputChannels;
199   UCHAR nFrameTimeSlots; /* e.g. 32 when used with HE-AAC */
200   UCHAR nQmfBands;
201   UCHAR nHybridBands;
202   UINT nFrameLength; /* number of output waveform samples/channel/frame */
203 
204   /* not Routing Sensible Switches/Varibles + lower Classes, secondary computed
205    */
206   INT nSamplesNext;
207   INT nAnalysisLengthTimeSlots;
208   INT nAnalysisLookaheadTimeSlots;
209   INT nUpdateHybridPositionTimeSlots;
210   INT *pnOutputBits;
211   INT nInputDelay;
212   INT nOutputBufferDelay;
213   INT nSurroundAnalysisBufferDelay;
214   INT nBitstreamDelayBuffer;
215   INT nBitstreamBufferRead;
216   INT nBitstreamBufferWrite;
217   INT nDiscardOutFrames;
218   INT avoid_keep;
219 
220   /* not Routing Sensible Switches/Varibles -> moved to lower Classes */
221   UCHAR useCoarseQuantCld;    /* Only Used in SpaceTreeSetup */
222   UCHAR useCoarseQuantIcc;    /* Only Used in SpaceTreeSetup */
223   UCHAR useCoarseQuantCpc;    /* Only Used in SpaceTreeSetup */
224   UCHAR useCoarseQuantArbDmx; /* ArbitraryDmx,... not available yet */
225   MP4SPACEENC_QUANTMODE
226   quantMode;          /* Used for quanitzation and in bitstream writer */
227   INT coreCoderDelay; /* Used in delay compensation */
228   INT timeAlignment;  /* Used in delay compensation */
229 
230   /* Local Processing Variables */
231   INT independencyCount;
232   INT independencyFlag;
233   INT **ppTrCurrPos;                /* belongs somehow to Onset Detection */
234   INT trPrevPos[2 * MAX_NUM_TRANS]; /* belongs somehow to Onset Detection */
235 
236   FRAMEWIN_LIST frameWinList;
237   SPATIALFRAME saveFrame;
238 
239   /* Module-Handles */
240   SPACE_TREE_SETUP spaceTreeSetup;
241   MPEG4SPACEENC_SSCBUF sscBuf;
242   FIXP_WIN *pFrameWindowAna__FDK[MAX_NUM_PARAMS];
243   HANDLE_QMF_FILTER_BANK *phQmfFiltIn__FDK;
244   HANDLE_DC_FILTER phDCFilterSigIn[SACENC_MAX_INPUT_CHANNELS];
245   HANDLE_ONSET_DETECT phOnset[SACENC_MAX_INPUT_CHANNELS];
246   HANDLE_SPACE_TREE hSpaceTree;
247   HANDLE_BSF_INSTANCE hBitstreamFormatter;
248   HANDLE_STATIC_GAIN_CONFIG hStaticGainConfig;
249   HANDLE_STATIC_GAIN hStaticGain;
250   HANDLE_DELAY hDelay;
251 
252   /* enhanced time domain downmix (for stereo input) */
253   HANDLE_ENHANCED_TIME_DOMAIN_DMX hEnhancedTimeDmx;
254 
255   /* Data Buffers */
256   INT_PCM **ppTimeSigIn__FDK;
257   INT_PCM **ppTimeSigDelayIn__FDK;
258   INT_PCM **ppTimeSigOut__FDK;
259   FIXP_DPK ***pppHybridIn__FDK;
260   FIXP_DPK ***pppHybridInStatic__FDK;
261   FIXP_DPK ***pppProcDataIn__FDK;
262   INT_PCM *pOutputDelayBuffer__FDK;
263 
264   UCHAR **ppBitstreamDelayBuffer;
265 
266   UCHAR *pParameterBand2HybridBandOffset;
267   INT staticGainScale;
268 
269   INT *pEncoderInputChScale;
270   INT *staticTimeDomainDmxInScale;
271 };
272 
273 /* Constants *****************************************************************/
274 static const UCHAR pValidBands_Ld[8] = {4, 5, 7, 9, 12, 15, 23, 40};
275 
276 static const UCHAR qmf2qmf[] = /* Bypass the HybridAnylyis/Synthesis*/
277     {0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,
278      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
279      30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
280      45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
281      60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,
282      75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,  87,  88,  89,
283      90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104,
284      105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
285      120, 121, 122, 123, 124, 125, 126, 127};
286 
287 /* Function / Class Declarations *********************************************/
288 static FDK_SACENC_ERROR mp4SpaceEnc_create(
289     HANDLE_MP4SPACE_ENCODER *phMp4SpaceEnc);
290 
291 static FDK_SACENC_ERROR FillSpatialSpecificConfig(
292     const HANDLE_MP4SPACE_ENCODER hEnc, SPATIALSPECIFICCONFIG *const hSsc);
293 
294 static FDK_SACENC_ERROR mp4SpaceEnc_FillSpaceTreeSetup(
295     const HANDLE_MP4SPACE_ENCODER hEnc,
296     SPACE_TREE_SETUP *const hSpaceTreeSetup);
297 
298 static FDK_SACENC_ERROR mp4SpaceEnc_InitDelayCompensation(
299     HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc, const INT coreCoderDelay);
300 
301 static FDK_SACENC_ERROR mp4SpaceEnc_InitDefault(
302     HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc);
303 
304 static DECORRCONFIG mp4SpaceEnc_GetDecorrConfig(const MP4SPACEENC_MODE encMode);
305 
306 static FDK_SACENC_ERROR mp4SpaceEnc_InitNumParamBands(
307     HANDLE_MP4SPACE_ENCODER hEnc, const MP4SPACEENC_BANDS_CONFIG nParamBands);
308 
309 /* Function / Class Definition ***********************************************/
mp4SpaceEnc_GetNumQmfBands(const UINT nSampleRate)310 static UINT mp4SpaceEnc_GetNumQmfBands(const UINT nSampleRate) {
311   UINT nQmfBands = 0;
312 
313   if (nSampleRate < 27713)
314     nQmfBands = 32;
315   else if (nSampleRate < 55426)
316     nQmfBands = 64;
317 
318   return nQmfBands;
319 }
320 
updateQmfFlags(const UINT flags,const INT keepStates)321 static UINT updateQmfFlags(const UINT flags, const INT keepStates) {
322   UINT qmfFlags = flags;
323 
324   qmfFlags = (qmfFlags & (~(UINT)QMF_FLAG_LP));
325   qmfFlags = (qmfFlags | QMF_FLAG_MPSLDFB);
326   qmfFlags = (keepStates) ? (qmfFlags | QMF_FLAG_KEEP_STATES)
327                           : (qmfFlags & (~(UINT)QMF_FLAG_KEEP_STATES));
328 
329   return qmfFlags;
330 }
331 
freq2HybridBand(const UINT nFrequency,const UINT nSampleRate,const UINT nQmfBands)332 static INT freq2HybridBand(const UINT nFrequency, const UINT nSampleRate,
333                            const UINT nQmfBands) {
334   /*
335     nQmfSlotWidth = (nSampleRate/2) / nQmfBands;
336     nQmfBand      = nFrequency / nQmfSlotWidth;
337   */
338   int nHybridBand = -1;
339   int scale = 0;
340   const FIXP_DBL temp = fDivNorm((FIXP_DBL)(2 * nFrequency * nQmfBands),
341                                  (FIXP_DBL)nSampleRate, &scale);
342   const int nQmfBand = scaleValue(temp, scale - (DFRACT_BITS - 1));
343 
344   if ((nQmfBand > -1) && (nQmfBand < (int)nQmfBands)) {
345     nHybridBand = qmf2qmf[nQmfBand];
346   }
347 
348   return nHybridBand;
349 }
350 
351 /*
352  * Examine buffer descriptor regarding choosen type.
353  *
354  * \param pBufDesc              Pointer to buffer descriptor
355  * \param type                  Buffer type to look for.
356 
357  * \return - Buffer descriptor index.
358  *         -1, if there is no entry available.
359  */
getBufDescIdx(const FDK_bufDescr * pBufDesc,const UINT type)360 static INT getBufDescIdx(const FDK_bufDescr *pBufDesc, const UINT type) {
361   INT i, idx = -1;
362 
363   for (i = 0; i < (int)pBufDesc->numBufs; i++) {
364     if (pBufDesc->pBufType[i] == type) {
365       idx = i;
366       break;
367     }
368   }
369   return idx;
370 }
371 
FDK_sacenc_open(HANDLE_MP4SPACE_ENCODER * phMp4SpaceEnc)372 FDK_SACENC_ERROR FDK_sacenc_open(HANDLE_MP4SPACE_ENCODER *phMp4SpaceEnc) {
373   return mp4SpaceEnc_create(phMp4SpaceEnc);
374 }
375 
mp4SpaceEnc_create(HANDLE_MP4SPACE_ENCODER * phMp4SpaceEnc)376 static FDK_SACENC_ERROR mp4SpaceEnc_create(
377     HANDLE_MP4SPACE_ENCODER *phMp4SpaceEnc) {
378   FDK_SACENC_ERROR error = SACENC_OK;
379   HANDLE_MP4SPACE_ENCODER hEnc = NULL;
380   ENC_CONFIG_SETUP setup;
381 
382   if (NULL == phMp4SpaceEnc) {
383     error = SACENC_INVALID_HANDLE;
384   } else {
385     int i, ch;
386     FDKmemclear(&setup, sizeof(ENC_CONFIG_SETUP));
387 
388     /* Allocate Encoder Instance */
389     FDK_ALLOCATE_MEMORY_1D(hEnc, 1, struct MP4SPACE_ENCODER);
390 
391     /* Clear everything, also pointers. */
392     if (NULL != hEnc) {
393       FDKmemclear(hEnc, sizeof(struct MP4SPACE_ENCODER));
394     }
395 
396     setup.maxSamplingrate = 48000;
397     setup.maxFrameTimeSlots = 16;
398 
399     setup.maxAnalysisLengthTimeSlots = 3 * setup.maxFrameTimeSlots;
400     setup.maxQmfBands = mp4SpaceEnc_GetNumQmfBands(setup.maxSamplingrate);
401     ;
402     setup.maxHybridBands = setup.maxQmfBands;
403     setup.maxFrameLength = setup.maxQmfBands * setup.maxFrameTimeSlots;
404 
405     setup.maxChIn = 2;
406     setup.maxChOut = 1;
407     setup.maxChTotOut = setup.maxChOut;
408     setup.bEncMode_212 = 1;
409     setup.maxHybridInStaticSlots = 24;
410 
411     /* Open Static Gain*/
412     if (SACENC_OK !=
413         (error = fdk_sacenc_staticGain_OpenConfig(&hEnc->hStaticGainConfig))) {
414       goto bail;
415     }
416 
417     /* enhanced time domain downmix (for stereo input) */
418     if (SACENC_OK != (error = fdk_sacenc_open_enhancedTimeDomainDmx(
419                           &hEnc->hEnhancedTimeDmx, setup.maxFrameLength))) {
420       goto bail;
421     }
422 
423     FDK_ALLOCATE_MEMORY_1D(hEnc->pParameterBand2HybridBandOffset,
424                            MAX_NUM_PARAM_BANDS, UCHAR);
425 
426     /* Create Space Tree first, to get number of in-/output channels */
427     if (SACENC_OK != (error = fdk_sacenc_spaceTree_Open(&hEnc->hSpaceTree))) {
428       goto bail;
429     }
430 
431     FDK_ALLOCATE_MEMORY_1D(hEnc->pEncoderInputChScale, setup.maxChIn, INT);
432     FDK_ALLOCATE_MEMORY_1D(hEnc->staticTimeDomainDmxInScale, setup.maxChIn,
433                            INT);
434 
435     FDK_ALLOCATE_MEMORY_1D(hEnc->phQmfFiltIn__FDK, setup.maxChIn,
436                            HANDLE_QMF_FILTER_BANK);
437 
438     /* Allocate Analysis Filterbank Structs */
439     for (ch = 0; ch < setup.maxChIn; ch++) {
440       FDK_ALLOCATE_MEMORY_1D_INT(hEnc->phQmfFiltIn__FDK[ch], 1,
441                                  struct QMF_FILTER_BANK, SECT_DATA_L2)
442       FDK_ALLOCATE_MEMORY_1D_INT(hEnc->phQmfFiltIn__FDK[ch]->FilterStates,
443                                  2 * 5 * setup.maxQmfBands, FIXP_QAS,
444                                  SECT_DATA_L2)
445     }
446 
447     /* Allocate Synthesis Filterbank Structs for arbitrary downmix */
448 
449     /* Allocate DC Filter Struct for normal signal input */
450     for (ch = 0; ch < setup.maxChIn; ch++) {
451       if (SACENC_OK !=
452           (error = fdk_sacenc_createDCFilter(&hEnc->phDCFilterSigIn[ch]))) {
453         goto bail;
454       }
455     }
456 
457     /* Open Onset Detection */
458     for (ch = 0; ch < setup.maxChIn; ch++) {
459       if (SACENC_OK != (error = fdk_sacenc_onsetDetect_Open(
460                             &hEnc->phOnset[ch], setup.maxFrameTimeSlots))) {
461         goto bail;
462       }
463     }
464 
465     FDK_ALLOCATE_MEMORY_2D(hEnc->ppTrCurrPos, setup.maxChIn, MAX_NUM_TRANS,
466                            INT);
467 
468     /* Create Windowing */
469     if (SACENC_OK !=
470         (error = fdk_sacenc_frameWindow_Create(&hEnc->hFrameWindow))) {
471       goto bail;
472     }
473 
474     /* Open static gain */
475     if (SACENC_OK != (error = fdk_sacenc_staticGain_Open(&hEnc->hStaticGain))) {
476       goto bail;
477     }
478 
479     /* create bitstream encoder */
480     if (SACENC_OK != (error = fdk_sacenc_createSpatialBitstreamEncoder(
481                           &hEnc->hBitstreamFormatter))) {
482       goto bail;
483     }
484 
485     FDK_ALLOCATE_MEMORY_1D(hEnc->sscBuf.pSsc, MAX_SSC_BYTES, UCHAR);
486 
487     {
488       FDK_ALLOCATE_MEMORY_2D(hEnc->ppTimeSigIn__FDK, setup.maxChIn,
489                              setup.maxFrameLength + MAX_DELAY_SURROUND_ANALYSIS,
490                              INT_PCM);
491     }
492     FDK_ALLOCATE_MEMORY_2D(hEnc->ppTimeSigDelayIn__FDK, setup.maxChIn,
493                            MAX_DELAY_SURROUND_ANALYSIS, INT_PCM);
494 
495     /* Create new buffers for several signals (including arbitrary downmix) */
496     if (setup.bEncMode_212 == 0) {
497       /* pOutputDelayBuffer__FDK buffer is not needed for SACENC_212 mode */
498       FDK_ALLOCATE_MEMORY_1D(
499           hEnc->pOutputDelayBuffer__FDK,
500           (setup.maxFrameLength + MAX_DELAY_OUTPUT) * setup.maxChOut, INT_PCM);
501     }
502 
503     /* allocate buffers */
504     if (setup.bEncMode_212 == 0) {
505       /* ppTimeSigOut__FDK buffer is not needed for SACENC_212 mode */
506       FDK_ALLOCATE_MEMORY_2D(hEnc->ppTimeSigOut__FDK, setup.maxChTotOut,
507                              setup.maxFrameLength, INT_PCM);
508     }
509 
510     if (setup.bEncMode_212 == 1) {
511       /* pppHybridIn__FDK buffer can be reduced by maxFrameTimeSlots/2 slots for
512        * SACENC_212 mode */
513       FDK_ALLOCATE_MEMORY_3D(
514           hEnc->pppHybridIn__FDK, setup.maxChIn,
515           setup.maxAnalysisLengthTimeSlots - (setup.maxFrameTimeSlots >> 1),
516           setup.maxHybridBands, FIXP_DPK);
517       FDK_ALLOCATE_MEMORY_3D(hEnc->pppHybridInStatic__FDK, setup.maxChIn,
518                              setup.maxHybridInStaticSlots, setup.maxHybridBands,
519                              FIXP_DPK);
520     } else {
521       FDK_ALLOCATE_MEMORY_3D(hEnc->pppHybridIn__FDK, setup.maxChIn,
522                              setup.maxAnalysisLengthTimeSlots,
523                              setup.maxHybridBands, FIXP_DPK);
524     }
525 
526     if (setup.bEncMode_212 == 0) {
527       /* pppProcDataIn__FDK buffer is not needed for SACENC_212 mode */
528       FDK_ALLOCATE_MEMORY_3D(hEnc->pppProcDataIn__FDK, MAX_SPACE_TREE_CHANNELS,
529                              setup.maxAnalysisLengthTimeSlots,
530                              setup.maxHybridBands, FIXP_DPK);
531     }
532     for (i = 0; i < MAX_NUM_PARAMS; i++) {
533       FDK_ALLOCATE_MEMORY_1D(hEnc->pFrameWindowAna__FDK[i],
534                              setup.maxAnalysisLengthTimeSlots, FIXP_WIN);
535     } /* for i */
536 
537     if (SACENC_OK != (error = fdk_sacenc_delay_Open(&hEnc->hDelay))) {
538       goto bail;
539     }
540 
541     if (setup.bEncMode_212 == 0) {
542       /* ppBitstreamDelayBuffer buffer is not needed for SACENC_212 mode */
543       FDK_ALLOCATE_MEMORY_2D(hEnc->ppBitstreamDelayBuffer, MAX_BITSTREAM_DELAY,
544                              MAX_MPEGS_BYTES, UCHAR);
545     }
546     FDK_ALLOCATE_MEMORY_1D(hEnc->pnOutputBits, MAX_BITSTREAM_DELAY, INT);
547 
548     hEnc->setup = setup; /* save configuration used while encoder allocation. */
549     mp4SpaceEnc_InitDefault(hEnc);
550 
551     if (NULL != phMp4SpaceEnc) {
552       *phMp4SpaceEnc = hEnc; /* return encoder handle */
553     }
554 
555   } /* valid handle */
556 
557   return error;
558 
559 bail:
560   if (NULL != hEnc) {
561     hEnc->setup = setup;
562     FDK_sacenc_close(&hEnc);
563   }
564   return ((SACENC_OK == error) ? SACENC_MEMORY_ERROR : error);
565 }
566 
mp4SpaceEnc_InitDefault(HANDLE_MP4SPACE_ENCODER hEnc)567 static FDK_SACENC_ERROR mp4SpaceEnc_InitDefault(HANDLE_MP4SPACE_ENCODER hEnc) {
568   FDK_SACENC_ERROR err = SACENC_OK;
569 
570   /* Get default static gain configuration. */
571   if (SACENC_OK != (err = fdk_sacenc_staticGain_InitDefaultConfig(
572                         hEnc->hStaticGainConfig))) {
573     goto bail;
574   }
575 
576 bail:
577   return err;
578 }
579 
FDK_sacenc_configure(HANDLE_MP4SPACE_ENCODER hEnc,const HANDLE_MP4SPACEENC_SETUP hSetup)580 static FDK_SACENC_ERROR FDK_sacenc_configure(
581     HANDLE_MP4SPACE_ENCODER hEnc, const HANDLE_MP4SPACEENC_SETUP hSetup) {
582   FDK_SACENC_ERROR error = SACENC_OK;
583 
584   hEnc->nSampleRate = hSetup->sampleRate;
585   hEnc->encMode = hSetup->encMode;
586   hEnc->nQmfBands = mp4SpaceEnc_GetNumQmfBands(hEnc->nSampleRate);
587 
588   /* Make sure that we have set time domain downmix for 212 */
589   if (hSetup->encMode == SACENC_212 && hSetup->bTimeDomainDmx == 0) {
590     error = SACENC_INVALID_CONFIG;
591   } else {
592     hEnc->useTimeDomDownmix = hSetup->bTimeDomainDmx;
593   }
594 
595   hEnc->timeAlignment = hSetup->timeAlignment;
596   hEnc->quantMode = hSetup->quantMode;
597 
598   hEnc->useCoarseQuantCld = hSetup->bUseCoarseQuant;
599   hEnc->useCoarseQuantCpc = hSetup->bUseCoarseQuant;
600   hEnc->useFrameKeep = (hSetup->bLdMode == 2);
601   hEnc->useCoarseQuantIcc = 0;    /* not available */
602   hEnc->useCoarseQuantArbDmx = 0; /* not available for user right now */
603   hEnc->independencyFactor = hSetup->independencyFactor;
604   hEnc->independencyCount = 0;
605   hEnc->independencyFlag = 1;
606 
607   /* set number of Hybrid bands */
608   hEnc->nHybridBands = hEnc->nQmfBands;
609   hEnc->nFrameTimeSlots = hSetup->frameTimeSlots;
610   mp4SpaceEnc_InitNumParamBands(hEnc, hSetup->nParamBands);
611 
612   return error;
613 }
614 
FDK_sacenc_init(HANDLE_MP4SPACE_ENCODER hEnc,const INT dmxDelay)615 FDK_SACENC_ERROR FDK_sacenc_init(HANDLE_MP4SPACE_ENCODER hEnc,
616                                  const INT dmxDelay) {
617   FDK_SACENC_ERROR error = SACENC_OK;
618 
619   /* Sanity Checks */
620   if (NULL == hEnc) {
621     error = SACENC_INVALID_HANDLE;
622   } else {
623     const int initStatesFlag = 1;
624 
625     int ch; /* loop counter */
626     int nChInArbDmx;
627 
628     if (SACENC_OK != (error = FDK_sacenc_configure(hEnc, &hEnc->user))) {
629       goto bail;
630     }
631 
632     hEnc->bEncMode_212_only = hEnc->setup.bEncMode_212;
633 
634     /* Slots per Frame and Frame Length */
635     if (hEnc->nFrameTimeSlots < 1) {
636       error = SACENC_INVALID_CONFIG;
637       goto bail;
638     }
639     hEnc->nFrameLength = hEnc->nQmfBands * hEnc->nFrameTimeSlots;
640 
641     if (hEnc->useFrameKeep == 1) {
642       hEnc->nAnalysisLengthTimeSlots = 3 * hEnc->nFrameTimeSlots;
643       hEnc->nUpdateHybridPositionTimeSlots = hEnc->nFrameTimeSlots;
644     } else {
645       hEnc->nAnalysisLengthTimeSlots = 2 * hEnc->nFrameTimeSlots;
646       hEnc->nUpdateHybridPositionTimeSlots = 0;
647     }
648 
649     {
650       hEnc->nAnalysisLookaheadTimeSlots =
651           hEnc->nAnalysisLengthTimeSlots - 3 * hEnc->nFrameTimeSlots / 2;
652     }
653 
654     /* init parameterBand2hybridBandOffset table */
655     fdk_sacenc_calcParameterBand2HybridBandOffset(
656         (BOX_SUBBAND_CONFIG)hEnc->nParamBands, hEnc->nHybridBands,
657         hEnc->pParameterBand2HybridBandOffset);
658 
659     /* Fill Setup structure for Space Tree */
660     if (SACENC_OK !=
661         (error = mp4SpaceEnc_FillSpaceTreeSetup(hEnc, &hEnc->spaceTreeSetup))) {
662       goto bail;
663     }
664 
665     /* Init space tree configuration */
666     if (SACENC_OK !=
667         (error = fdk_sacenc_spaceTree_Init(
668              hEnc->hSpaceTree, &hEnc->spaceTreeSetup,
669              hEnc->pParameterBand2HybridBandOffset, hEnc->useFrameKeep))) {
670       goto bail;
671     }
672 
673     /* Get space tree description and resulting number of input/output channels
674      */
675     {
676       SPACE_TREE_DESCRIPTION spaceTreeDescription;
677 
678       if (SACENC_OK != (error = fdk_sacenc_spaceTree_GetDescription(
679                             hEnc->hSpaceTree, &spaceTreeDescription))) {
680         goto bail;
681       }
682 
683       hEnc->nInputChannels =
684           spaceTreeDescription.nOutChannels; /* space tree description
685                                                 describes decoder
686                                                 configuration */
687       hEnc->nOutputChannels =
688           spaceTreeDescription.nInChannels; /* space tree description
689                                                describes decoder
690                                                configuration */
691     }
692 
693     nChInArbDmx = 0;
694 
695     /* INITIALIZATION */
696     for (ch = 0; ch < hEnc->nInputChannels; ch++) {
697       /* scaling in analysis qmf filterbank (7) */
698       hEnc->pEncoderInputChScale[ch] = 7;
699 
700       {
701         /* additional scaling in qmf prototype filter for low delay */
702         hEnc->pEncoderInputChScale[ch] += 1;
703       }
704 
705       { hEnc->pEncoderInputChScale[ch] += DC_FILTER_SF; }
706     } /* nInputChannels */
707 
708     /* Init analysis filterbank */
709     for (ch = 0; ch < hEnc->nInputChannels; ch++) {
710       hEnc->phQmfFiltIn__FDK[ch]->flags =
711           updateQmfFlags(hEnc->phQmfFiltIn__FDK[ch]->flags, !initStatesFlag);
712 
713       if (0 != qmfInitAnalysisFilterBank(
714                    hEnc->phQmfFiltIn__FDK[ch],
715                    (FIXP_QAS *)hEnc->phQmfFiltIn__FDK[ch]->FilterStates, 1,
716                    hEnc->nQmfBands, hEnc->nQmfBands, hEnc->nQmfBands,
717                    hEnc->phQmfFiltIn__FDK[ch]->flags)) {
718         error = SACENC_INIT_ERROR;
719         goto bail;
720       }
721     }
722 
723     /* Initialize DC Filter. */
724     {
725       for (ch = 0; ch < hEnc->nInputChannels; ch++) {
726         if (SACENC_OK != (error = fdk_sacenc_initDCFilter(
727                               hEnc->phDCFilterSigIn[ch], hEnc->nSampleRate))) {
728           goto bail;
729         }
730       }
731     }
732 
733     /* Init onset detect. */
734     {
735       /* init onset detect configuration struct */
736       ONSET_DETECT_CONFIG onsetDetectConfig;
737       onsetDetectConfig.maxTimeSlots = hEnc->nFrameTimeSlots;
738       onsetDetectConfig.lowerBoundOnsetDetection =
739           freq2HybridBand(1725, hEnc->nSampleRate, hEnc->nQmfBands);
740       onsetDetectConfig.upperBoundOnsetDetection = hEnc->nHybridBands;
741 
742       for (ch = 0; ch < hEnc->nInputChannels; ch++) {
743         if (SACENC_OK != (error = fdk_sacenc_onsetDetect_Init(
744                               hEnc->phOnset[ch], &onsetDetectConfig, 1))) {
745           goto bail;
746         }
747       }
748     }
749 
750     {
751       /* init windowing */
752       FRAMEWINDOW_CONFIG framewindowConfig;
753       framewindowConfig.nTimeSlotsMax = hEnc->nFrameTimeSlots;
754       framewindowConfig.bFrameKeep = hEnc->useFrameKeep;
755 
756       if (SACENC_OK != (error = fdk_sacenc_frameWindow_Init(
757                             hEnc->hFrameWindow, &framewindowConfig))) {
758         goto bail;
759       }
760     }
761 
762     /* Set encoder mode for static gain initialization. */
763     if (SACENC_OK != (error = fdk_sacenc_staticGain_SetEncMode(
764                           hEnc->hStaticGainConfig, hEnc->encMode))) {
765       goto bail;
766     }
767 
768     /* Init static gain. */
769     if (SACENC_OK != (error = fdk_sacenc_staticGain_Init(
770                           hEnc->hStaticGain, hEnc->hStaticGainConfig,
771                           &(hEnc->staticGainScale)))) {
772       goto bail;
773     }
774 
775     for (ch = 0; ch < hEnc->nInputChannels; ch++) {
776       hEnc->pEncoderInputChScale[ch] += hEnc->staticGainScale;
777     }
778 
779     /* enhanced downmix for stereo input*/
780     if (hEnc->useTimeDomDownmix != 0) {
781       if (SACENC_OK != (error = fdk_sacenc_init_enhancedTimeDomainDmx(
782                             hEnc->hEnhancedTimeDmx,
783                             fdk_sacenc_getPreGainPtrFDK(hEnc->hStaticGain),
784                             hEnc->staticGainScale,
785                             fdk_sacenc_getPostGainFDK(hEnc->hStaticGain),
786                             hEnc->staticGainScale, hEnc->nFrameLength))) {
787         goto bail;
788       }
789     }
790 
791     /* Create config structure for bitstream formatter including arbitrary
792      * downmix residual */
793     if (SACENC_OK != (error = fdk_sacenc_initSpatialBitstreamEncoder(
794                           hEnc->hBitstreamFormatter))) {
795       goto bail;
796     }
797 
798     if (SACENC_OK != (error = FillSpatialSpecificConfig(
799                           hEnc, fdk_sacenc_getSpatialSpecificConfig(
800                                     hEnc->hBitstreamFormatter)))) {
801       goto bail;
802     }
803 
804     if (SACENC_OK !=
805         (error = fdk_sacenc_writeSpatialSpecificConfig(
806              fdk_sacenc_getSpatialSpecificConfig(hEnc->hBitstreamFormatter),
807              hEnc->sscBuf.pSsc, MAX_SSC_BYTES, &hEnc->sscBuf.nSscSizeBits))) {
808       goto bail;
809     }
810 
811     /* init delay compensation with dmx core coder delay; if no core coder is
812      * used, many other buffers are initialized nevertheless */
813     if (SACENC_OK !=
814         (error = mp4SpaceEnc_InitDelayCompensation(hEnc, dmxDelay))) {
815       goto bail;
816     }
817 
818     /* How much input do we need? */
819     hEnc->nSamplesNext =
820         hEnc->nFrameLength * (hEnc->nInputChannels + nChInArbDmx);
821     hEnc->nSamplesValid = 0;
822   } /* valid handle */
823 
824 bail:
825   return error;
826 }
827 
getAnalysisLengthTimeSlots(FIXP_WIN * pFrameWindowAna,INT nTimeSlots)828 static INT getAnalysisLengthTimeSlots(FIXP_WIN *pFrameWindowAna,
829                                       INT nTimeSlots) {
830   int i;
831   for (i = nTimeSlots - 1; i >= 0; i--) {
832     if (pFrameWindowAna[i] != (FIXP_WIN)0) {
833       break;
834     }
835   }
836   nTimeSlots = i + 1;
837   return nTimeSlots;
838 }
839 
getAnalysisStartTimeSlot(FIXP_WIN * pFrameWindowAna,INT nTimeSlots)840 static INT getAnalysisStartTimeSlot(FIXP_WIN *pFrameWindowAna, INT nTimeSlots) {
841   int startTimeSlot = 0;
842   int i;
843   for (i = 0; i < nTimeSlots; i++) {
844     if (pFrameWindowAna[i] != (FIXP_WIN)0) {
845       break;
846     }
847   }
848   startTimeSlot = i;
849   return startTimeSlot;
850 }
851 
__FeedDeinterPreScale(HANDLE_MP4SPACE_ENCODER hEnc,INT_PCM const * const pSamples,INT_PCM * const pOutputSamples,INT const nSamples,UINT const isInputInterleaved,UINT const inputBufferSizePerChannel,UINT * const pnSamplesFed)852 static FDK_SACENC_ERROR __FeedDeinterPreScale(
853     HANDLE_MP4SPACE_ENCODER hEnc, INT_PCM const *const pSamples,
854     INT_PCM *const pOutputSamples, INT const nSamples,
855     UINT const isInputInterleaved, UINT const inputBufferSizePerChannel,
856     UINT *const pnSamplesFed) {
857   FDK_SACENC_ERROR error = SACENC_OK;
858 
859   if ((hEnc == NULL) || (pSamples == NULL) || (pnSamplesFed == NULL)) {
860     error = SACENC_INVALID_HANDLE;
861   } else if (nSamples == 0) {
862     error = SACENC_INVALID_CONFIG; /* Flushing not implemented */
863   } else {
864     int ch;
865     const INT nChIn = hEnc->nInputChannels;
866     const INT nChInWithDmx = nChIn;
867     const INT samplesToFeed =
868         FDKmin(nSamples, hEnc->nSamplesNext - hEnc->nSamplesValid);
869     const INT nSamplesPerChannel = samplesToFeed / nChInWithDmx;
870 
871     if ((samplesToFeed < 0) || (samplesToFeed % nChInWithDmx != 0) ||
872         (samplesToFeed > nChInWithDmx * (INT)hEnc->nFrameLength)) {
873       error = SACENC_INVALID_CONFIG;
874       goto bail;
875     }
876     int i;
877 
878     const INT_PCM *pInput__FDK;
879     const INT_PCM *pInput2__FDK;
880 
881     { /* no dmx align = default*/
882       pInput__FDK = pSamples;
883       pInput2__FDK = pSamples + (hEnc->nInputDelay * nChInWithDmx);
884     }
885 
886     for (i = 0; i < hEnc->nInputChannels; i++) {
887       hEnc->staticTimeDomainDmxInScale[i] = hEnc->staticGainScale;
888     }
889 
890     /*****        N-channel-input     *****/
891     for (ch = 0; ch < nChIn; ch++) {
892       /* Write delayed time signal into time signal buffer */
893       FDKmemcpy(&(hEnc->ppTimeSigIn__FDK[ch][0]),
894                 &(hEnc->ppTimeSigDelayIn__FDK[ch][0]),
895                 hEnc->nSurroundAnalysisBufferDelay * sizeof(INT_PCM));
896 
897       if (isInputInterleaved) {
898         /* Add the new frame de-interleaved. Apply nSurroundAnalysisBufferDelay.
899          */
900         FDKmemcpy_flex(
901             &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nSurroundAnalysisBufferDelay]),
902             1, pInput__FDK + ch, nChInWithDmx, hEnc->nInputDelay);
903         FDKmemcpy_flex(
904             &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nSurroundAnalysisBufferDelay +
905                                          hEnc->nInputDelay]),
906             1, pInput2__FDK + ch, nChInWithDmx,
907             nSamplesPerChannel - hEnc->nInputDelay);
908       } else {
909         /* Input is already deinterleaved, just copy */
910         FDKmemcpy(
911             &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nSurroundAnalysisBufferDelay]),
912             pInput__FDK + ch * inputBufferSizePerChannel,
913             hEnc->nInputDelay * sizeof(INT_PCM));
914         FDKmemcpy(
915             &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nSurroundAnalysisBufferDelay +
916                                          hEnc->nInputDelay]),
917             pInput2__FDK + ch * inputBufferSizePerChannel,
918             (nSamplesPerChannel - hEnc->nInputDelay) * sizeof(INT_PCM));
919       }
920 
921       /* Update time signal delay buffer */
922       FDKmemcpy(&(hEnc->ppTimeSigDelayIn__FDK[ch][0]),
923                 &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nFrameLength]),
924                 hEnc->nSurroundAnalysisBufferDelay * sizeof(INT_PCM));
925     } /* for ch */
926 
927     /*****      No Arbitrary Downmix      *****/
928     /* "Crude TD Dmx": Time DomainDownmix + NO Arbitrary Downmix, Delay Added at
929      * pOutputBuffer */
930     if ((hEnc->useTimeDomDownmix > 0)) {
931       if ((hEnc->useTimeDomDownmix == 1) || (hEnc->nInputChannels != 2)) {
932         error = SACENC_INVALID_CONFIG;
933         goto bail;
934       } else {
935         /* enhanced time domain downmix (for stereo input) */
936         if (hEnc->encMode == SACENC_212) {
937           if (pOutputSamples == NULL) {
938             error = SACENC_INVALID_HANDLE;
939             goto bail;
940           }
941 
942           fdk_sacenc_apply_enhancedTimeDomainDmx(
943               hEnc->hEnhancedTimeDmx, hEnc->ppTimeSigIn__FDK, pOutputSamples,
944               hEnc->nSurroundAnalysisBufferDelay);
945         } else {
946           if (&hEnc->ppTimeSigOut__FDK[0][0] == NULL) {
947             error = SACENC_INVALID_HANDLE;
948             goto bail;
949           }
950 
951           fdk_sacenc_apply_enhancedTimeDomainDmx(
952               hEnc->hEnhancedTimeDmx, hEnc->ppTimeSigIn__FDK,
953               &hEnc->ppTimeSigOut__FDK[0][0],
954               hEnc->nSurroundAnalysisBufferDelay);
955         }
956       }
957     }
958 
959     /* update number of samples still to process */
960     hEnc->nSamplesValid += samplesToFeed;
961 
962     /*return number of fed samples */
963     *pnSamplesFed = samplesToFeed;
964   }
965 bail:
966   return error;
967 }
968 
FDK_sacenc_encode(const HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,const FDK_bufDescr * inBufDesc,const FDK_bufDescr * outBufDesc,const SACENC_InArgs * inargs,SACENC_OutArgs * outargs)969 FDK_SACENC_ERROR FDK_sacenc_encode(const HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,
970                                    const FDK_bufDescr *inBufDesc,
971                                    const FDK_bufDescr *outBufDesc,
972                                    const SACENC_InArgs *inargs,
973                                    SACENC_OutArgs *outargs) {
974   FDK_SACENC_ERROR error = SACENC_OK;
975 
976   const INT_PCM *pInputSamples =
977       (const INT_PCM *)inBufDesc->ppBase[getBufDescIdx(
978           inBufDesc, (FDK_BUF_TYPE_INPUT | FDK_BUF_TYPE_PCM_DATA))];
979 
980   INT_PCM *const pOutputSamples = (INT_PCM *)outBufDesc->ppBase[getBufDescIdx(
981       outBufDesc, (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_PCM_DATA))];
982 
983   const int nOutputSamplesBufferSize =
984       outBufDesc->pBufSize[getBufDescIdx(
985           outBufDesc, (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_PCM_DATA))] /
986       outBufDesc->pEleSize[getBufDescIdx(
987           outBufDesc, (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_PCM_DATA))];
988 
989   if ((hMp4SpaceEnc == NULL) || (pInputSamples == NULL)) {
990     error = SACENC_INVALID_HANDLE;
991   } else {
992     int nOutputSamples;
993     int i, ch, ps, winCnt, ts, slot;
994     INT currTransPos = -1;
995     SPATIALFRAME *pFrameData = NULL;
996 
997     /* Improve Code Readability */
998     const int nChIn = hMp4SpaceEnc->nInputChannels;
999     const int nChInWithDmx = nChIn;
1000     const int nChOut = hMp4SpaceEnc->nOutputChannels;
1001     const int nSamplesPerChannel = inargs->nInputSamples / nChInWithDmx;
1002     const int nOutputSamplesMax = nSamplesPerChannel * nChOut;
1003     const int nFrameTimeSlots = hMp4SpaceEnc->nFrameTimeSlots;
1004 
1005     INT encoderInputChScale[SACENC_MAX_INPUT_CHANNELS];
1006     INT nFrameTimeSlotsReduction = 0;
1007 
1008     if (hMp4SpaceEnc->encMode == SACENC_212) {
1009       nFrameTimeSlotsReduction = hMp4SpaceEnc->nFrameTimeSlots >> 1;
1010     }
1011 
1012     for (i = 0; i < nChIn; i++)
1013       encoderInputChScale[i] = hMp4SpaceEnc->pEncoderInputChScale[i];
1014 
1015     /* Sanity Check */
1016     if ((0 != inargs->nInputSamples % nChInWithDmx)) {
1017       error = SACENC_INVALID_CONFIG;
1018       goto bail;
1019     }
1020 
1021     /*
1022      * Get Frame Data Handle.
1023      */
1024 
1025     /* get bitstream handle (for storage of cld's, icc's and so on)
1026      * get spatialframe 2 frames in the future; NOTE: this is necessary to
1027      * synchronise spatial data and audio data */
1028     if (NULL == (pFrameData = fdk_sacenc_getSpatialFrame(
1029                      hMp4SpaceEnc->hBitstreamFormatter, WRITE_SPATIALFRAME))) {
1030       error = SACENC_INVALID_HANDLE;
1031       goto bail;
1032     }
1033 
1034     /* Independent Frames Counters*/
1035     if (hMp4SpaceEnc->nDiscardOutFrames >
1036         0) { /* Independent Frames if they should be discarded, Reset Counter*/
1037       hMp4SpaceEnc->independencyCount =
1038           0; /* Reset the counter, first valid frame is an independent one*/
1039       hMp4SpaceEnc->independencyFlag = 1;
1040     } else { /*hMp4SpaceEnc->nDiscardOutFrames == 0*/
1041       hMp4SpaceEnc->independencyFlag =
1042           (hMp4SpaceEnc->independencyCount == 0) ? 1 : 0;
1043       if (hMp4SpaceEnc->independencyFactor > 0) {
1044         hMp4SpaceEnc->independencyCount++;
1045         hMp4SpaceEnc->independencyCount =
1046             hMp4SpaceEnc->independencyCount %
1047             ((int)hMp4SpaceEnc->independencyFactor);
1048       } else { /* independencyFactor == 0 */
1049         hMp4SpaceEnc->independencyCount = -1;
1050       }
1051     }
1052 
1053     /*
1054      * Time signal preprocessing:
1055      * - Feed input buffer
1056      * - Prescale time signal
1057      * - Apply DC filter on input signal
1058      */
1059 
1060     /* Feed, Deinterleave, Pre-Scale the input time signals */
1061     if (SACENC_OK !=
1062         (error = __FeedDeinterPreScale(
1063              hMp4SpaceEnc, pInputSamples, pOutputSamples, inargs->nInputSamples,
1064              inargs->isInputInterleaved, inargs->inputBufferSizePerChannel,
1065              &outargs->nSamplesConsumed))) {
1066       goto bail;
1067     }
1068 
1069     if (hMp4SpaceEnc->nSamplesNext != hMp4SpaceEnc->nSamplesValid) {
1070       error = SACENC_INVALID_CONFIG;
1071       goto bail;
1072     }
1073 
1074     if (hMp4SpaceEnc->encMode == SACENC_212 &&
1075         hMp4SpaceEnc->bEncMode_212_only) {
1076       for (ch = 0; ch < nChIn; ch++) {
1077         for (slot = 0; slot < nFrameTimeSlots; slot++) {
1078           setCplxVec(
1079               hMp4SpaceEnc->pppHybridIn__FDK
1080                   [ch][hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
1081                        nFrameTimeSlots - nFrameTimeSlotsReduction + slot],
1082               (FIXP_DBL)0, hMp4SpaceEnc->nHybridBands);
1083         }
1084       }
1085     }
1086 
1087     /*
1088      * Time / Frequency:
1089      * - T/F audio input channels
1090      * - T/F arbitrary downmix input channels
1091      */
1092     for (ch = 0; ch < nChIn; ch++) {
1093       C_AALLOC_SCRATCH_START(pQmfInReal, FIXP_DBL, MAX_QMF_BANDS)
1094       C_AALLOC_SCRATCH_START(pQmfInImag, FIXP_DBL, MAX_QMF_BANDS)
1095       FIXP_GAIN *pPreGain =
1096           fdk_sacenc_getPreGainPtrFDK(hMp4SpaceEnc->hStaticGain);
1097 
1098       for (ts = 0; ts < nFrameTimeSlots; ts++) {
1099         FIXP_DBL *pSpecReal;
1100         FIXP_DBL *pSpecImag;
1101 
1102         INT_PCM *pTimeIn =
1103             &hMp4SpaceEnc->ppTimeSigIn__FDK[ch][(ts * hMp4SpaceEnc->nQmfBands)];
1104 
1105         {
1106           /* Apply DC filter on input channels */
1107           if (SACENC_OK != (error = fdk_sacenc_applyDCFilter(
1108                                 hMp4SpaceEnc->phDCFilterSigIn[ch], pTimeIn,
1109                                 pTimeIn, hMp4SpaceEnc->nQmfBands))) {
1110             goto bail;
1111           }
1112         }
1113 
1114         /* QMF filterbank */
1115         C_ALLOC_SCRATCH_START(pWorkBuffer, FIXP_DBL, (MAX_QMF_BANDS << 1));
1116 
1117         qmfAnalysisFilteringSlot(hMp4SpaceEnc->phQmfFiltIn__FDK[ch], pQmfInReal,
1118                                  pQmfInImag, pTimeIn, 1, pWorkBuffer);
1119 
1120         C_ALLOC_SCRATCH_END(pWorkBuffer, FIXP_DBL, (MAX_QMF_BANDS << 1));
1121 
1122         pSpecReal = pQmfInReal;
1123         pSpecImag = pQmfInImag;
1124 
1125         /* Apply pre-scale after filterbank */
1126         if (MAXVAL_GAIN != pPreGain[ch]) {
1127           for (i = 0; i < hMp4SpaceEnc->nHybridBands; i++) {
1128             hMp4SpaceEnc
1129                 ->pppHybridIn__FDK[ch]
1130                                   [hMp4SpaceEnc->nAnalysisLookaheadTimeSlots +
1131                                    ts][i]
1132                 .v.re = fMult(pSpecReal[i], pPreGain[ch]);
1133             hMp4SpaceEnc
1134                 ->pppHybridIn__FDK[ch]
1135                                   [hMp4SpaceEnc->nAnalysisLookaheadTimeSlots +
1136                                    ts][i]
1137                 .v.im = fMult(pSpecImag[i], pPreGain[ch]);
1138           }
1139         } else {
1140           for (i = 0; i < hMp4SpaceEnc->nHybridBands; i++) {
1141             hMp4SpaceEnc
1142                 ->pppHybridIn__FDK[ch]
1143                                   [hMp4SpaceEnc->nAnalysisLookaheadTimeSlots +
1144                                    ts][i]
1145                 .v.re = pSpecReal[i];
1146             hMp4SpaceEnc
1147                 ->pppHybridIn__FDK[ch]
1148                                   [hMp4SpaceEnc->nAnalysisLookaheadTimeSlots +
1149                                    ts][i]
1150                 .v.im = pSpecImag[i];
1151           }
1152         }
1153       } /* ts */
1154       C_AALLOC_SCRATCH_END(pQmfInImag, FIXP_DBL, MAX_QMF_BANDS)
1155       C_AALLOC_SCRATCH_END(pQmfInReal, FIXP_DBL, MAX_QMF_BANDS)
1156 
1157       if (SACENC_OK != error) {
1158         goto bail;
1159       }
1160     } /* ch */
1161 
1162     if (hMp4SpaceEnc->encMode == SACENC_212 &&
1163         hMp4SpaceEnc->bEncMode_212_only) {
1164       for (ch = 0; ch < nChIn; ch++) {
1165         for (slot = 0;
1166              slot < (int)(hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
1167                           nFrameTimeSlots - nFrameTimeSlotsReduction);
1168              slot++) {
1169           copyCplxVec(hMp4SpaceEnc->pppHybridIn__FDK[ch][slot],
1170                       hMp4SpaceEnc->pppHybridInStatic__FDK[ch][slot],
1171                       hMp4SpaceEnc->nHybridBands);
1172         }
1173       }
1174       for (ch = 0; ch < nChIn; ch++) {
1175         for (slot = 0;
1176              slot < (int)(hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
1177                           nFrameTimeSlots - nFrameTimeSlotsReduction);
1178              slot++) {
1179           copyCplxVec(
1180               hMp4SpaceEnc->pppHybridInStatic__FDK[ch][slot],
1181               hMp4SpaceEnc->pppHybridIn__FDK[ch][nFrameTimeSlots + slot],
1182               hMp4SpaceEnc->nHybridBands);
1183         }
1184       }
1185     }
1186 
1187     /*
1188      * Onset Detection:
1189      * - detection of transients
1190      * - build framing
1191      */
1192     for (ch = 0; ch < nChIn; ch++) {
1193       if (ch != 3) { /* !LFE */
1194         if (SACENC_OK !=
1195             (error = fdk_sacenc_onsetDetect_Apply(
1196                  hMp4SpaceEnc->phOnset[ch], nFrameTimeSlots,
1197                  hMp4SpaceEnc->nHybridBands,
1198                  &hMp4SpaceEnc->pppHybridIn__FDK
1199                       [ch][hMp4SpaceEnc->nAnalysisLookaheadTimeSlots],
1200                  encoderInputChScale[ch],
1201                  hMp4SpaceEnc->trPrevPos[1], /* contains previous Transient */
1202                  hMp4SpaceEnc->ppTrCurrPos[ch]))) {
1203           goto bail;
1204         }
1205 
1206         if ((1) && (hMp4SpaceEnc->useFrameKeep == 0)) {
1207           hMp4SpaceEnc->ppTrCurrPos[ch][0] = -1;
1208         }
1209 
1210         /* Find first Transient Position */
1211         if ((hMp4SpaceEnc->ppTrCurrPos[ch][0] >= 0) &&
1212             ((currTransPos < 0) ||
1213              (hMp4SpaceEnc->ppTrCurrPos[ch][0] < currTransPos))) {
1214           currTransPos = hMp4SpaceEnc->ppTrCurrPos[ch][0];
1215         }
1216       } /* !LFE */
1217     }   /* ch */
1218 
1219     if (hMp4SpaceEnc->useFrameKeep == 1) {
1220       if ((currTransPos != -1) || (hMp4SpaceEnc->independencyFlag == 1)) {
1221         hMp4SpaceEnc->avoid_keep = NUM_KEEP_WINDOWS;
1222         currTransPos = -1;
1223       }
1224     }
1225 
1226     /* Save previous Transient Position */
1227     hMp4SpaceEnc->trPrevPos[0] =
1228         FDKmax(-1, hMp4SpaceEnc->trPrevPos[1] - (INT)nFrameTimeSlots);
1229     hMp4SpaceEnc->trPrevPos[1] = currTransPos;
1230 
1231     /* Update Onset Detection Energy Buffer */
1232     for (ch = 0; ch < nChIn; ch++) {
1233       if (SACENC_OK != (error = fdk_sacenc_onsetDetect_Update(
1234                             hMp4SpaceEnc->phOnset[ch], nFrameTimeSlots))) {
1235         goto bail;
1236       }
1237     }
1238 
1239     /* Framing */
1240     if (SACENC_OK !=
1241         (error = fdk_sacenc_frameWindow_GetWindow(
1242              hMp4SpaceEnc->hFrameWindow, hMp4SpaceEnc->trPrevPos,
1243              nFrameTimeSlots, &pFrameData->framingInfo,
1244              hMp4SpaceEnc->pFrameWindowAna__FDK, &hMp4SpaceEnc->frameWinList,
1245              hMp4SpaceEnc->avoid_keep))) {
1246       goto bail;
1247     }
1248 
1249     /*
1250      * MPS Processing:
1251      */
1252     for (ps = 0, winCnt = 0; ps < hMp4SpaceEnc->frameWinList.n; ++ps) {
1253       /* Analysis Windowing */
1254       if (hMp4SpaceEnc->frameWinList.dat[ps].hold == FW_HOLD) {
1255         /* ************************************** */
1256         /* ONLY COPY AND HOLD PREVIOUS PARAMETERS */
1257         if (SACENC_OK != (error = fdk_sacenc_duplicateParameterSet(
1258                               &hMp4SpaceEnc->saveFrame, 0, pFrameData, ps))) {
1259           goto bail;
1260         }
1261 
1262       } else { /* !FW_HOLD */
1263         /* ************************************** */
1264         /* NEW WINDOW */
1265 
1266         INT nAnalysisLengthTimeSlots, analysisStartTimeSlot;
1267 
1268         nAnalysisLengthTimeSlots = getAnalysisLengthTimeSlots(
1269             hMp4SpaceEnc->pFrameWindowAna__FDK[winCnt],
1270             hMp4SpaceEnc->nAnalysisLengthTimeSlots);
1271 
1272         analysisStartTimeSlot =
1273             getAnalysisStartTimeSlot(hMp4SpaceEnc->pFrameWindowAna__FDK[winCnt],
1274                                      hMp4SpaceEnc->nAnalysisLengthTimeSlots);
1275 
1276         /* perform main signal analysis windowing in
1277          * fdk_sacenc_spaceTree_Apply() */
1278         FIXP_WIN *pFrameWindowAna__FDK =
1279             hMp4SpaceEnc->pFrameWindowAna__FDK[winCnt];
1280         FIXP_DPK ***pppHybridIn__FDK = hMp4SpaceEnc->pppHybridIn__FDK;
1281         FIXP_DPK ***pppProcDataIn__FDK = hMp4SpaceEnc->pppProcDataIn__FDK;
1282 
1283         if (hMp4SpaceEnc->encMode == SACENC_212 &&
1284             hMp4SpaceEnc->bEncMode_212_only) {
1285           pppProcDataIn__FDK = pppHybridIn__FDK;
1286         }
1287 
1288         if (SACENC_OK !=
1289             (error = fdk_sacenc_spaceTree_Apply(
1290                  hMp4SpaceEnc->hSpaceTree, ps, nChIn, nAnalysisLengthTimeSlots,
1291                  analysisStartTimeSlot, hMp4SpaceEnc->nHybridBands,
1292                  pFrameWindowAna__FDK, pppHybridIn__FDK,
1293                  pppProcDataIn__FDK, /* multi-channel input */
1294                  pFrameData, hMp4SpaceEnc->avoid_keep, encoderInputChScale))) {
1295           goto bail;
1296         }
1297 
1298         /* Save spatial frame for potential hold parameter set */
1299         if (SACENC_OK != (error = fdk_sacenc_duplicateParameterSet(
1300                               pFrameData, ps, &hMp4SpaceEnc->saveFrame, 0))) {
1301           goto bail;
1302         }
1303 
1304         ++winCnt;
1305       }
1306       if (hMp4SpaceEnc->avoid_keep > 0) {
1307         hMp4SpaceEnc->avoid_keep--;
1308       }
1309     } /* Loop over Parameter Sets */
1310     /* ---- End of Processing Loop ---- */
1311 
1312     /*
1313      * Update hybridInReal/Imag buffer and do the same for arbDmx
1314      * this means to move the hybrid data of the current frame to the beginning
1315      * of the 2*nFrameLength-long buffer
1316      */
1317     if (!(hMp4SpaceEnc->encMode == SACENC_212 &&
1318           hMp4SpaceEnc->bEncMode_212_only)) {
1319       for (ch = 0; ch < nChIn; ch++) { /* for automatic downmix */
1320         for (slot = 0;
1321              slot < (int)(hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
1322                           nFrameTimeSlots - nFrameTimeSlotsReduction);
1323              slot++) {
1324           copyCplxVec(
1325               hMp4SpaceEnc->pppHybridIn__FDK[ch][slot],
1326               hMp4SpaceEnc->pppHybridIn__FDK[ch][nFrameTimeSlots + slot],
1327               hMp4SpaceEnc->nHybridBands);
1328         }
1329         for (slot = 0; slot < nFrameTimeSlots; slot++) {
1330           setCplxVec(
1331               hMp4SpaceEnc->pppHybridIn__FDK
1332                   [ch][hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
1333                        nFrameTimeSlots - nFrameTimeSlotsReduction + slot],
1334               (FIXP_DBL)0, hMp4SpaceEnc->nHybridBands);
1335         }
1336       }
1337     }
1338     /*
1339      * Spatial Tonality:
1340      */
1341     {
1342       /* Smooth config off. */
1343       FDKmemclear(&pFrameData->smgData, sizeof(pFrameData->smgData));
1344     }
1345 
1346     /*
1347      * Create bitstream
1348      * - control independecy flag
1349      * - write spatial frame
1350      * - return bitstream
1351      */
1352     UCHAR *pBitstreamDelayBuffer;
1353 
1354     if (hMp4SpaceEnc->encMode == SACENC_212) {
1355       /* no bitstream delay buffer for SACENC_212 mode, write bitstream directly
1356        * into the sacOutBuffer buffer which is provided by the core routine */
1357       pBitstreamDelayBuffer = (UCHAR *)outBufDesc->ppBase[1];
1358     } else {
1359       /* bitstream delay is handled in ppBitstreamDelayBuffer buffer */
1360       pBitstreamDelayBuffer =
1361           hMp4SpaceEnc
1362               ->ppBitstreamDelayBuffer[hMp4SpaceEnc->nBitstreamBufferWrite];
1363     }
1364     if (pBitstreamDelayBuffer == NULL) {
1365       error = SACENC_INVALID_HANDLE;
1366       goto bail;
1367     }
1368 
1369     pFrameData->bsIndependencyFlag = hMp4SpaceEnc->independencyFlag;
1370 
1371     if (SACENC_OK !=
1372         (error = fdk_sacenc_writeSpatialFrame(
1373              pBitstreamDelayBuffer, MAX_MPEGS_BYTES,
1374              &hMp4SpaceEnc->pnOutputBits[hMp4SpaceEnc->nBitstreamBufferWrite],
1375              hMp4SpaceEnc->hBitstreamFormatter))) {
1376       goto bail;
1377     }
1378 
1379     /* return bitstream info */
1380     if ((hMp4SpaceEnc->nDiscardOutFrames == 0) &&
1381         (getBufDescIdx(outBufDesc,
1382                        (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_BS_DATA)) != -1)) {
1383       const INT idx = getBufDescIdx(
1384           outBufDesc, (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_BS_DATA));
1385       const INT outBits =
1386           hMp4SpaceEnc->pnOutputBits[hMp4SpaceEnc->nBitstreamBufferRead];
1387 
1388       if (((outBits + 7) / 8) >
1389           (INT)(outBufDesc->pBufSize[idx] / outBufDesc->pEleSize[idx])) {
1390         outargs->nOutputBits = 0;
1391         error = SACENC_ENCODE_ERROR;
1392         goto bail;
1393       }
1394 
1395       /* return bitstream buffer, copy delayed bitstream for all configurations
1396        * except for the SACENC_212 mode */
1397       if (hMp4SpaceEnc->encMode != SACENC_212) {
1398         FDKmemcpy(
1399             outBufDesc->ppBase[idx],
1400             hMp4SpaceEnc
1401                 ->ppBitstreamDelayBuffer[hMp4SpaceEnc->nBitstreamBufferRead],
1402             (outBits + 7) / 8);
1403       }
1404 
1405       /* return number of valid bits */
1406       outargs->nOutputBits = outBits;
1407     } else { /* No spatial data should be returned if the current frame is to be
1408                 discarded. */
1409       outargs->nOutputBits = 0;
1410     }
1411 
1412     /* update pointers */
1413     hMp4SpaceEnc->nBitstreamBufferRead =
1414         (hMp4SpaceEnc->nBitstreamBufferRead + 1) %
1415         hMp4SpaceEnc->nBitstreamDelayBuffer;
1416     hMp4SpaceEnc->nBitstreamBufferWrite =
1417         (hMp4SpaceEnc->nBitstreamBufferWrite + 1) %
1418         hMp4SpaceEnc->nBitstreamDelayBuffer;
1419 
1420     /* Set Output Parameters */
1421     nOutputSamples =
1422         (hMp4SpaceEnc->nDiscardOutFrames == 0)
1423             ? (nOutputSamplesMax)
1424             : 0; /* don't output samples in case frames to be discarded */
1425     if (nOutputSamples > nOutputSamplesBufferSize) {
1426       error = SACENC_INVALID_CONFIG;
1427       goto bail;
1428     }
1429     outargs->nOutputSamples = nOutputSamples;
1430 
1431     { /* !bQmfOutput */
1432 
1433       if (hMp4SpaceEnc->encMode != SACENC_212) {
1434         /* delay output samples and interleave them */
1435         /* note: in case of arbitrary downmix this will always be processed,
1436          * because nOutputSamples != 0, even if bDMXAlign is switched on */
1437         /* always run copy-func, so nOutputSamplesMax instead of nOutputSamples
1438          */
1439         for (ch = 0; ch < nChOut; ch++) {
1440           FDKmemcpy_flex(
1441               &hMp4SpaceEnc->pOutputDelayBuffer__FDK
1442                    [ch + (hMp4SpaceEnc->nOutputBufferDelay) * nChOut],
1443               nChOut, hMp4SpaceEnc->ppTimeSigOut__FDK[ch], 1,
1444               nOutputSamplesMax / nChOut);
1445         }
1446 
1447         /* write delayed data in output pcm stream */
1448         /* always calculate, limiter must have a lookahead!!! */
1449         FDKmemcpy(pOutputSamples, hMp4SpaceEnc->pOutputDelayBuffer__FDK,
1450                   nOutputSamplesMax * sizeof(INT_PCM));
1451 
1452         /* update delay buffer (move back end to the beginning of the buffer) */
1453         FDKmemmove(
1454             hMp4SpaceEnc->pOutputDelayBuffer__FDK,
1455             &hMp4SpaceEnc->pOutputDelayBuffer__FDK[nOutputSamplesMax],
1456             nChOut * (hMp4SpaceEnc->nOutputBufferDelay) * sizeof(INT_PCM));
1457       }
1458 
1459       if (hMp4SpaceEnc->useTimeDomDownmix <= 0) {
1460         if (SACENC_OK != (error = fdk_sacenc_staticPostGain_ApplyFDK(
1461                               hMp4SpaceEnc->hStaticGain, pOutputSamples,
1462                               nOutputSamplesMax, 0))) {
1463           goto bail;
1464         }
1465       }
1466 
1467     } /* !bQmfOutput */
1468 
1469     if (hMp4SpaceEnc->nDiscardOutFrames > 0) {
1470       hMp4SpaceEnc->nDiscardOutFrames--;
1471     }
1472 
1473     /* Invalidate Input Buffer */
1474     hMp4SpaceEnc->nSamplesValid = 0;
1475 
1476   } /* valid handle */
1477 bail:
1478   return error;
1479 }
1480 
FDK_sacenc_close(HANDLE_MP4SPACE_ENCODER * phMp4SpaceEnc)1481 FDK_SACENC_ERROR FDK_sacenc_close(HANDLE_MP4SPACE_ENCODER *phMp4SpaceEnc) {
1482   FDK_SACENC_ERROR error = SACENC_OK;
1483 
1484   if (NULL != phMp4SpaceEnc) {
1485     if (NULL != *phMp4SpaceEnc) {
1486       int ch, i;
1487       HANDLE_MP4SPACE_ENCODER const hEnc = *phMp4SpaceEnc;
1488 
1489       if (hEnc->pParameterBand2HybridBandOffset != NULL) {
1490         FDK_FREE_MEMORY_1D(hEnc->pParameterBand2HybridBandOffset);
1491       }
1492       /* Free Analysis Filterbank Structs */
1493       if (hEnc->pEncoderInputChScale != NULL) {
1494         FDK_FREE_MEMORY_1D(hEnc->pEncoderInputChScale);
1495       }
1496       if (hEnc->staticTimeDomainDmxInScale != NULL) {
1497         FDK_FREE_MEMORY_1D(hEnc->staticTimeDomainDmxInScale);
1498       }
1499       if (hEnc->phQmfFiltIn__FDK != NULL) {
1500         for (ch = 0; ch < hEnc->setup.maxChIn; ch++) {
1501           if (hEnc->phQmfFiltIn__FDK[ch] != NULL) {
1502             if (hEnc->phQmfFiltIn__FDK[ch]->FilterStates != NULL) {
1503               FDK_FREE_MEMORY_1D(hEnc->phQmfFiltIn__FDK[ch]->FilterStates);
1504             }
1505             FDK_FREE_MEMORY_1D(hEnc->phQmfFiltIn__FDK[ch]);
1506           }
1507         }
1508         FDK_FREE_MEMORY_1D(hEnc->phQmfFiltIn__FDK);
1509       }
1510       for (ch = 0; ch < hEnc->setup.maxChIn; ch++) {
1511         if (NULL != hEnc->phDCFilterSigIn[ch]) {
1512           fdk_sacenc_destroyDCFilter(&hEnc->phDCFilterSigIn[ch]);
1513         }
1514       }
1515       /* Close Onset Detection */
1516       for (ch = 0; ch < hEnc->setup.maxChIn; ch++) {
1517         if (NULL != hEnc->phOnset[ch]) {
1518           fdk_sacenc_onsetDetect_Close(&hEnc->phOnset[ch]);
1519         }
1520       }
1521       if (hEnc->ppTrCurrPos) {
1522         FDK_FREE_MEMORY_2D(hEnc->ppTrCurrPos);
1523       }
1524       if (hEnc->hFrameWindow) {
1525         fdk_sacenc_frameWindow_Destroy(&hEnc->hFrameWindow);
1526       }
1527       /* Close Space Tree */
1528       if (NULL != hEnc->hSpaceTree) {
1529         fdk_sacenc_spaceTree_Close(&hEnc->hSpaceTree);
1530       }
1531       if (NULL != hEnc->hEnhancedTimeDmx) {
1532         fdk_sacenc_close_enhancedTimeDomainDmx(&hEnc->hEnhancedTimeDmx);
1533       }
1534       /* Close Static Gain */
1535       if (NULL != hEnc->hStaticGain) {
1536         fdk_sacenc_staticGain_Close(&hEnc->hStaticGain);
1537       }
1538       if (NULL != hEnc->hStaticGainConfig) {
1539         fdk_sacenc_staticGain_CloseConfig(&hEnc->hStaticGainConfig);
1540       }
1541       /* Close Delay*/
1542       if (NULL != hEnc->hDelay) {
1543         fdk_sacenc_delay_Close(&hEnc->hDelay);
1544       }
1545       /* Delete Bitstream Stuff */
1546       if (NULL != hEnc->hBitstreamFormatter) {
1547         fdk_sacenc_destroySpatialBitstreamEncoder(&(hEnc->hBitstreamFormatter));
1548       }
1549       if (hEnc->pppHybridIn__FDK != NULL) {
1550         if (hEnc->setup.bEncMode_212 == 1) {
1551           FDK_FREE_MEMORY_3D(hEnc->pppHybridIn__FDK);
1552           FDK_FREE_MEMORY_3D(hEnc->pppHybridInStatic__FDK);
1553         } else {
1554           FDK_FREE_MEMORY_3D(hEnc->pppHybridIn__FDK);
1555         }
1556       }
1557       if (hEnc->pppProcDataIn__FDK != NULL) {
1558         FDK_FREE_MEMORY_3D(hEnc->pppProcDataIn__FDK);
1559       }
1560       if (hEnc->pOutputDelayBuffer__FDK != NULL) {
1561         FDK_FREE_MEMORY_1D(hEnc->pOutputDelayBuffer__FDK);
1562       }
1563       if (hEnc->ppTimeSigIn__FDK != NULL) {
1564         { FDK_FREE_MEMORY_2D(hEnc->ppTimeSigIn__FDK); }
1565       }
1566       if (hEnc->ppTimeSigDelayIn__FDK != NULL) {
1567         FDK_FREE_MEMORY_2D(hEnc->ppTimeSigDelayIn__FDK);
1568       }
1569       if (hEnc->ppTimeSigOut__FDK != NULL) {
1570         FDK_FREE_MEMORY_2D(hEnc->ppTimeSigOut__FDK);
1571       }
1572       for (i = 0; i < MAX_NUM_PARAMS; i++) {
1573         if (hEnc->pFrameWindowAna__FDK[i] != NULL) {
1574           FDK_FREE_MEMORY_1D(hEnc->pFrameWindowAna__FDK[i]);
1575         }
1576       }
1577       if (hEnc->pnOutputBits != NULL) {
1578         FDK_FREE_MEMORY_1D(hEnc->pnOutputBits);
1579       }
1580       if (hEnc->ppBitstreamDelayBuffer != NULL) {
1581         FDK_FREE_MEMORY_2D(hEnc->ppBitstreamDelayBuffer);
1582       }
1583       if (hEnc->sscBuf.pSsc != NULL) {
1584         FDK_FREE_MEMORY_1D(hEnc->sscBuf.pSsc);
1585       }
1586       FDK_FREE_MEMORY_1D(*phMp4SpaceEnc);
1587     }
1588   }
1589 
1590   return error;
1591 }
1592 
1593 /*-----------------------------------------------------------------------------
1594   functionname: mp4SpaceEnc_InitDelayCompensation()
1595   description:  initialzes delay compensation
1596   returns:      noError on success, an apropriate error code else
1597   -----------------------------------------------------------------------------*/
mp4SpaceEnc_InitDelayCompensation(HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,const INT coreCoderDelay)1598 static FDK_SACENC_ERROR mp4SpaceEnc_InitDelayCompensation(
1599     HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc, const INT coreCoderDelay) {
1600   FDK_SACENC_ERROR error = SACENC_OK;
1601 
1602   /* Sanity Check */
1603   if (hMp4SpaceEnc == NULL) {
1604     error = SACENC_INVALID_HANDLE;
1605   } else {
1606     hMp4SpaceEnc->coreCoderDelay = coreCoderDelay;
1607 
1608     if (SACENC_OK != (error = fdk_sacenc_delay_Init(
1609                           hMp4SpaceEnc->hDelay, hMp4SpaceEnc->nQmfBands,
1610                           hMp4SpaceEnc->nFrameLength, coreCoderDelay,
1611                           hMp4SpaceEnc->timeAlignment))) {
1612       goto bail;
1613     }
1614 
1615     fdk_sacenc_delay_SetDmxAlign(hMp4SpaceEnc->hDelay, 0);
1616     fdk_sacenc_delay_SetTimeDomDmx(
1617         hMp4SpaceEnc->hDelay, (hMp4SpaceEnc->useTimeDomDownmix >= 1) ? 1 : 0);
1618     fdk_sacenc_delay_SetMinimizeDelay(hMp4SpaceEnc->hDelay, 1);
1619 
1620     if (SACENC_OK != (error = fdk_sacenc_delay_SubCalulateBufferDelays(
1621                           hMp4SpaceEnc->hDelay))) {
1622       goto bail;
1623     }
1624 
1625     /* init output delay compensation */
1626     hMp4SpaceEnc->nBitstreamDelayBuffer =
1627         fdk_sacenc_delay_GetBitstreamFrameBufferSize(hMp4SpaceEnc->hDelay);
1628     hMp4SpaceEnc->nOutputBufferDelay =
1629         fdk_sacenc_delay_GetOutputAudioBufferDelay(hMp4SpaceEnc->hDelay);
1630     hMp4SpaceEnc->nSurroundAnalysisBufferDelay =
1631         fdk_sacenc_delay_GetSurroundAnalysisBufferDelay(hMp4SpaceEnc->hDelay);
1632     hMp4SpaceEnc->nBitstreamBufferRead = 0;
1633     hMp4SpaceEnc->nBitstreamBufferWrite =
1634         hMp4SpaceEnc->nBitstreamDelayBuffer - 1;
1635 
1636     if (hMp4SpaceEnc->encMode == SACENC_212) {
1637       /* mode 212 expects no bitstream delay */
1638       if (hMp4SpaceEnc->nBitstreamBufferWrite !=
1639           hMp4SpaceEnc->nBitstreamBufferRead) {
1640         error = SACENC_PARAM_ERROR;
1641         goto bail;
1642       }
1643 
1644       /* mode 212 expects no output buffer delay */
1645       if (hMp4SpaceEnc->nOutputBufferDelay != 0) {
1646         error = SACENC_PARAM_ERROR;
1647         goto bail;
1648       }
1649     }
1650 
1651     /*** Input delay to obtain a net encoder delay that is a multiple
1652     of the used framelength to ensure synchronization of framing
1653     in artistic down-mix with the corresponding spatial data.      ***/
1654     hMp4SpaceEnc->nDiscardOutFrames =
1655         fdk_sacenc_delay_GetDiscardOutFrames(hMp4SpaceEnc->hDelay);
1656     hMp4SpaceEnc->nInputDelay =
1657         fdk_sacenc_delay_GetDmxAlignBufferDelay(hMp4SpaceEnc->hDelay);
1658 
1659     /* reset independency Flag counter */
1660     hMp4SpaceEnc->independencyCount = 0;
1661     hMp4SpaceEnc->independencyFlag = 1;
1662 
1663     int i;
1664 
1665     /* write some parameters to bitstream */
1666     for (i = 0; i < hMp4SpaceEnc->nBitstreamDelayBuffer - 1; i++) {
1667       SPATIALFRAME *pFrameData = NULL;
1668 
1669       if (NULL == (pFrameData = fdk_sacenc_getSpatialFrame(
1670                        hMp4SpaceEnc->hBitstreamFormatter, READ_SPATIALFRAME))) {
1671         error = SACENC_INVALID_HANDLE;
1672         goto bail;
1673       }
1674 
1675       pFrameData->bsIndependencyFlag = 1;
1676       pFrameData->framingInfo.numParamSets = 1;
1677       pFrameData->framingInfo.bsFramingType = 0;
1678 
1679       fdk_sacenc_writeSpatialFrame(
1680           hMp4SpaceEnc->ppBitstreamDelayBuffer[i], MAX_MPEGS_BYTES,
1681           &hMp4SpaceEnc->pnOutputBits[i], hMp4SpaceEnc->hBitstreamFormatter);
1682     }
1683 
1684     if ((hMp4SpaceEnc->nInputDelay > MAX_DELAY_INPUT) ||
1685         (hMp4SpaceEnc->nOutputBufferDelay > MAX_DELAY_OUTPUT) ||
1686         (hMp4SpaceEnc->nSurroundAnalysisBufferDelay >
1687          MAX_DELAY_SURROUND_ANALYSIS) ||
1688         (hMp4SpaceEnc->nBitstreamDelayBuffer > MAX_BITSTREAM_DELAY)) {
1689       error = SACENC_INIT_ERROR;
1690       goto bail;
1691     }
1692   }
1693 
1694 bail:
1695 
1696   return error;
1697 }
1698 
__mapQuantMode(const MP4SPACEENC_QUANTMODE quantMode)1699 static QUANTMODE __mapQuantMode(const MP4SPACEENC_QUANTMODE quantMode) {
1700   QUANTMODE bsQuantMode = QUANTMODE_INVALID;
1701 
1702   switch (quantMode) {
1703     case SACENC_QUANTMODE_FINE:
1704       bsQuantMode = QUANTMODE_FINE;
1705       break;
1706     case SACENC_QUANTMODE_EBQ1:
1707       bsQuantMode = QUANTMODE_EBQ1;
1708       break;
1709     case SACENC_QUANTMODE_EBQ2:
1710       bsQuantMode = QUANTMODE_EBQ2;
1711       break;
1712     case SACENC_QUANTMODE_RSVD3:
1713     case SACENC_QUANTMODE_INVALID:
1714     default:
1715       bsQuantMode = QUANTMODE_INVALID;
1716   } /* switch hEnc->quantMode */
1717 
1718   return bsQuantMode;
1719 }
1720 
FillSpatialSpecificConfig(const HANDLE_MP4SPACE_ENCODER hEnc,SPATIALSPECIFICCONFIG * const hSsc)1721 static FDK_SACENC_ERROR FillSpatialSpecificConfig(
1722     const HANDLE_MP4SPACE_ENCODER hEnc, SPATIALSPECIFICCONFIG *const hSsc) {
1723   FDK_SACENC_ERROR error = SACENC_OK;
1724 
1725   if ((NULL == hEnc) || (NULL == hSsc)) {
1726     error = SACENC_INVALID_HANDLE;
1727   } else {
1728     SPACE_TREE_DESCRIPTION spaceTreeDescription;
1729     int i;
1730 
1731     /* Get tree description */
1732     if (SACENC_OK != (error = fdk_sacenc_spaceTree_GetDescription(
1733                           hEnc->hSpaceTree, &spaceTreeDescription))) {
1734       goto bail;
1735     }
1736 
1737     /* Fill SSC */
1738     FDKmemclear(hSsc, sizeof(SPATIALSPECIFICCONFIG)); /* reset */
1739 
1740     hSsc->numBands = hEnc->spaceTreeSetup.nParamBands; /* for bsFreqRes */
1741 
1742     /* Fill tree configuration */
1743     hSsc->treeDescription.numOttBoxes = spaceTreeDescription.nOttBoxes;
1744     hSsc->treeDescription.numInChan = spaceTreeDescription.nInChannels;
1745     hSsc->treeDescription.numOutChan = spaceTreeDescription.nOutChannels;
1746 
1747     for (i = 0; i < SACENC_MAX_NUM_BOXES; i++) {
1748       hSsc->ottConfig[i].bsOttBands = hSsc->numBands;
1749     }
1750 
1751     switch (hEnc->encMode) {
1752       case SACENC_212:
1753         hSsc->bsTreeConfig = TREE_212;
1754         break;
1755       case SACENC_INVALID_MODE:
1756       default:
1757         error = SACENC_INVALID_CONFIG;
1758         goto bail;
1759     }
1760 
1761     hSsc->bsSamplingFrequency =
1762         hEnc->nSampleRate; /* for bsSamplingFrequencyIndex */
1763     hSsc->bsFrameLength = hEnc->nFrameTimeSlots - 1;
1764 
1765     /* map decorr type */
1766     if (DECORR_INVALID ==
1767         (hSsc->bsDecorrConfig = mp4SpaceEnc_GetDecorrConfig(hEnc->encMode))) {
1768       error = SACENC_INVALID_CONFIG;
1769       goto bail;
1770     }
1771 
1772     /* map quantMode */
1773     if (QUANTMODE_INVALID ==
1774         (hSsc->bsQuantMode = __mapQuantMode(hEnc->quantMode))) {
1775       error = SACENC_INVALID_CONFIG;
1776       goto bail;
1777     }
1778 
1779     /* Configure Gains*/
1780     hSsc->bsFixedGainDMX = fdk_sacenc_staticGain_GetDmxGain(hEnc->hStaticGain);
1781     hSsc->bsEnvQuantMode = 0;
1782 
1783   } /* valid handle */
1784 
1785 bail:
1786   return error;
1787 }
1788 
mp4SpaceEnc_FillSpaceTreeSetup(const HANDLE_MP4SPACE_ENCODER hEnc,SPACE_TREE_SETUP * const hSpaceTreeSetup)1789 static FDK_SACENC_ERROR mp4SpaceEnc_FillSpaceTreeSetup(
1790     const HANDLE_MP4SPACE_ENCODER hEnc,
1791     SPACE_TREE_SETUP *const hSpaceTreeSetup) {
1792   FDK_SACENC_ERROR error = SACENC_OK;
1793 
1794   /* Sanity Check */
1795   if (NULL == hEnc || NULL == hSpaceTreeSetup) {
1796     error = SACENC_INVALID_HANDLE;
1797   } else {
1798     QUANTMODE tmpQuantmode = QUANTMODE_INVALID;
1799 
1800     /* map quantMode */
1801     if (QUANTMODE_INVALID == (tmpQuantmode = __mapQuantMode(hEnc->quantMode))) {
1802       error = SACENC_INVALID_CONFIG;
1803       goto bail;
1804     }
1805 
1806     hSpaceTreeSetup->nParamBands = hEnc->nParamBands;
1807     hSpaceTreeSetup->bUseCoarseQuantTtoCld = hEnc->useCoarseQuantCld;
1808     hSpaceTreeSetup->bUseCoarseQuantTtoIcc = hEnc->useCoarseQuantIcc;
1809     hSpaceTreeSetup->quantMode = tmpQuantmode;
1810     hSpaceTreeSetup->nHybridBandsMax = hEnc->nHybridBands;
1811 
1812     switch (hEnc->encMode) {
1813       case SACENC_212:
1814         hSpaceTreeSetup->mode = SPACETREE_212;
1815         hSpaceTreeSetup->nChannelsInMax = 2;
1816         break;
1817       case SACENC_INVALID_MODE:
1818       default:
1819         error = SACENC_INVALID_CONFIG;
1820         goto bail;
1821     } /* switch hEnc->encMode */
1822 
1823   } /* valid handle */
1824 bail:
1825   return error;
1826 }
1827 
FDK_sacenc_getInfo(const HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,MP4SPACEENC_INFO * const pInfo)1828 FDK_SACENC_ERROR FDK_sacenc_getInfo(const HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,
1829                                     MP4SPACEENC_INFO *const pInfo) {
1830   FDK_SACENC_ERROR error = SACENC_OK;
1831 
1832   if ((NULL == hMp4SpaceEnc) || (NULL == pInfo)) {
1833     error = SACENC_INVALID_HANDLE;
1834   } else {
1835     pInfo->nSampleRate = hMp4SpaceEnc->nSampleRate;
1836     pInfo->nSamplesFrame = hMp4SpaceEnc->nFrameLength;
1837     pInfo->nTotalInputChannels = hMp4SpaceEnc->nInputChannels;
1838     pInfo->nDmxDelay = fdk_sacenc_delay_GetInfoDmxDelay(hMp4SpaceEnc->hDelay);
1839     pInfo->nCodecDelay =
1840         fdk_sacenc_delay_GetInfoCodecDelay(hMp4SpaceEnc->hDelay);
1841     pInfo->nDecoderDelay =
1842         fdk_sacenc_delay_GetInfoDecoderDelay(hMp4SpaceEnc->hDelay);
1843     pInfo->nPayloadDelay =
1844         fdk_sacenc_delay_GetBitstreamFrameBufferSize(hMp4SpaceEnc->hDelay) - 1;
1845     pInfo->nDiscardOutFrames = hMp4SpaceEnc->nDiscardOutFrames;
1846 
1847     pInfo->pSscBuf = &hMp4SpaceEnc->sscBuf;
1848   }
1849   return error;
1850 }
1851 
FDK_sacenc_setParam(HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,const SPACEENC_PARAM param,const UINT value)1852 FDK_SACENC_ERROR FDK_sacenc_setParam(HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,
1853                                      const SPACEENC_PARAM param,
1854                                      const UINT value) {
1855   FDK_SACENC_ERROR error = SACENC_OK;
1856 
1857   /* check encoder handle */
1858   if (hMp4SpaceEnc == NULL) {
1859     error = SACENC_INVALID_HANDLE;
1860     goto bail;
1861   }
1862 
1863   /* apply param value */
1864   switch (param) {
1865     case SACENC_LOWDELAY:
1866       if (!((value == 0) || (value == 1) || (value == 2))) {
1867         error = SACENC_INVALID_CONFIG;
1868         break;
1869       }
1870       hMp4SpaceEnc->user.bLdMode = value;
1871       break;
1872 
1873     case SACENC_ENC_MODE:
1874       switch ((MP4SPACEENC_MODE)value) {
1875         case SACENC_212:
1876           hMp4SpaceEnc->user.encMode = (MP4SPACEENC_MODE)value;
1877           break;
1878         default:
1879           error = SACENC_INVALID_CONFIG;
1880       }
1881       break;
1882 
1883     case SACENC_SAMPLERATE:
1884       if (((int)value < 0) ||
1885           ((int)value > hMp4SpaceEnc->setup.maxSamplingrate)) {
1886         error = SACENC_INVALID_CONFIG;
1887         break;
1888       }
1889       hMp4SpaceEnc->user.sampleRate = value;
1890       break;
1891 
1892     case SACENC_FRAME_TIME_SLOTS:
1893       if (((int)value < 0) ||
1894           ((int)value > hMp4SpaceEnc->setup.maxFrameTimeSlots)) {
1895         error = SACENC_INVALID_CONFIG;
1896         break;
1897       }
1898       hMp4SpaceEnc->user.frameTimeSlots = value;
1899       break;
1900 
1901     case SACENC_PARAM_BANDS:
1902       switch ((MP4SPACEENC_BANDS_CONFIG)value) {
1903         case SACENC_BANDS_4:
1904         case SACENC_BANDS_5:
1905         case SACENC_BANDS_7:
1906         case SACENC_BANDS_9:
1907         case SACENC_BANDS_12:
1908         case SACENC_BANDS_15:
1909         case SACENC_BANDS_23:
1910           hMp4SpaceEnc->user.nParamBands = (MP4SPACEENC_BANDS_CONFIG)value;
1911           break;
1912         default:
1913           error = SACENC_INVALID_CONFIG;
1914       }
1915       break;
1916 
1917     case SACENC_TIME_DOM_DMX:
1918       if (!((value == 0) || (value == 2))) {
1919         error = SACENC_INVALID_CONFIG;
1920         break;
1921       }
1922       hMp4SpaceEnc->user.bTimeDomainDmx = value;
1923       break;
1924 
1925     case SACENC_DMX_GAIN:
1926       if (!((value == 0) || (value == 1) || (value == 2) || (value == 3) ||
1927             (value == 4) || (value == 5) || (value == 6) || (value == 7))) {
1928         error = SACENC_INVALID_CONFIG;
1929         break;
1930       }
1931       error = fdk_sacenc_staticGain_SetDmxGain(hMp4SpaceEnc->hStaticGainConfig,
1932                                                (MP4SPACEENC_DMX_GAIN)value);
1933       break;
1934 
1935     case SACENC_COARSE_QUANT:
1936       if (!((value == 0) || (value == 1))) {
1937         error = SACENC_INVALID_CONFIG;
1938         break;
1939       }
1940       hMp4SpaceEnc->user.bUseCoarseQuant = value;
1941       break;
1942 
1943     case SACENC_QUANT_MODE:
1944       switch ((MP4SPACEENC_QUANTMODE)value) {
1945         case SACENC_QUANTMODE_FINE:
1946         case SACENC_QUANTMODE_EBQ1:
1947         case SACENC_QUANTMODE_EBQ2:
1948           hMp4SpaceEnc->user.quantMode = (MP4SPACEENC_QUANTMODE)value;
1949           break;
1950         default:
1951           error = SACENC_INVALID_CONFIG;
1952       }
1953       break;
1954 
1955     case SACENC_TIME_ALIGNMENT:
1956       if ((INT)value < -32768 || (INT)value > 32767) {
1957         error = SACENC_INVALID_CONFIG;
1958         break;
1959       }
1960       hMp4SpaceEnc->user.timeAlignment = value;
1961       break;
1962 
1963     case SACENC_INDEPENDENCY_COUNT:
1964       hMp4SpaceEnc->independencyCount = value;
1965       break;
1966 
1967     case SACENC_INDEPENDENCY_FACTOR:
1968       hMp4SpaceEnc->user.independencyFactor = value;
1969       break;
1970 
1971     default:
1972       error = SACENC_UNSUPPORTED_PARAMETER;
1973       break;
1974   } /* switch(param) */
1975 bail:
1976   return error;
1977 }
1978 
FDK_sacenc_getLibInfo(LIB_INFO * info)1979 FDK_SACENC_ERROR FDK_sacenc_getLibInfo(LIB_INFO *info) {
1980   int i = 0;
1981 
1982   if (info == NULL) {
1983     return SACENC_INVALID_HANDLE;
1984   }
1985 
1986   FDK_toolsGetLibInfo(info);
1987 
1988   /* search for next free tab */
1989   for (i = 0; i < FDK_MODULE_LAST; i++) {
1990     if (info[i].module_id == FDK_NONE) break;
1991   }
1992   if (i == FDK_MODULE_LAST) {
1993     return SACENC_INIT_ERROR;
1994   }
1995 
1996   info[i].module_id = FDK_MPSENC;
1997   info[i].build_date = SACENC_LIB_BUILD_DATE;
1998   info[i].build_time = SACENC_LIB_BUILD_TIME;
1999   info[i].title = SACENC_LIB_TITLE;
2000   info[i].version = LIB_VERSION(SACENC_LIB_VL0, SACENC_LIB_VL1, SACENC_LIB_VL2);
2001   LIB_VERSION_STRING(&info[i]);
2002 
2003   /* Capability flags */
2004   info[i].flags = 0;
2005   /* End of flags */
2006 
2007   return SACENC_OK;
2008 }
2009 
mp4SpaceEnc_GetDecorrConfig(const MP4SPACEENC_MODE encMode)2010 static DECORRCONFIG mp4SpaceEnc_GetDecorrConfig(
2011     const MP4SPACEENC_MODE encMode) {
2012   DECORRCONFIG decorrConfig = DECORR_INVALID;
2013 
2014   /* set decorrConfig dependent on tree mode */
2015   switch (encMode) {
2016     case SACENC_212:
2017       decorrConfig = DECORR_QMFSPLIT0;
2018       break;
2019     case SACENC_INVALID_MODE:
2020     default:
2021       decorrConfig = DECORR_INVALID;
2022   }
2023   return decorrConfig;
2024 }
2025 
mp4SpaceEnc_InitNumParamBands(HANDLE_MP4SPACE_ENCODER hEnc,const MP4SPACEENC_BANDS_CONFIG nParamBands)2026 static FDK_SACENC_ERROR mp4SpaceEnc_InitNumParamBands(
2027     HANDLE_MP4SPACE_ENCODER hEnc, const MP4SPACEENC_BANDS_CONFIG nParamBands) {
2028   FDK_SACENC_ERROR error = SACENC_OK;
2029 
2030   /* Set/Check nParamBands */
2031   int k = 0;
2032   const int n = sizeof(pValidBands_Ld) / sizeof(UCHAR);
2033   const UCHAR *pBands = pValidBands_Ld;
2034 
2035   while (k < n && pBands[k] != (UCHAR)nParamBands) ++k;
2036   if (k == n) {
2037     hEnc->nParamBands = SACENC_BANDS_INVALID;
2038   } else {
2039     hEnc->nParamBands = nParamBands;
2040   }
2041   return error;
2042 }
2043