summaryrefslogtreecommitdiffstats
path: root/fdk-aac/libSACenc/src/sacenc_lib.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fdk-aac/libSACenc/src/sacenc_lib.cpp')
-rw-r--r--fdk-aac/libSACenc/src/sacenc_lib.cpp2042
1 files changed, 2042 insertions, 0 deletions
diff --git a/fdk-aac/libSACenc/src/sacenc_lib.cpp b/fdk-aac/libSACenc/src/sacenc_lib.cpp
new file mode 100644
index 0000000..d6a1658
--- /dev/null
+++ b/fdk-aac/libSACenc/src/sacenc_lib.cpp
@@ -0,0 +1,2042 @@
+/* -----------------------------------------------------------------------------
+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
+----------------------------------------------------------------------------- */
+
+/*********************** MPEG surround encoder library *************************
+
+ Author(s): Max Neuendorf
+
+ Description: Encoder Library Interface
+ Interface to Spacial Audio Coding Encoder lib
+
+*******************************************************************************/
+
+/****************************************************************************
+\file
+Description of file contents
+******************************************************************************/
+
+/* Includes ******************************************************************/
+#include "sacenc_lib.h"
+#include "sacenc_const.h"
+#include "genericStds.h"
+#include "FDK_core.h"
+#include "sacenc_tree.h"
+#include "sacenc_bitstream.h"
+#include "sacenc_onsetdetect.h"
+#include "sacenc_framewindowing.h"
+#include "sacenc_filter.h"
+#include "sacenc_paramextract.h"
+#include "sacenc_staticgain.h"
+#include "sacenc_delay.h"
+#include "sacenc_dmx_tdom_enh.h"
+#include "sacenc_vectorfunctions.h"
+#include "qmf.h"
+
+/* Defines *******************************************************************/
+
+/* Encoder library info */
+#define SACENC_LIB_VL0 2
+#define SACENC_LIB_VL1 0
+#define SACENC_LIB_VL2 0
+#define SACENC_LIB_TITLE "MPEG Surround Encoder"
+#ifdef __ANDROID__
+#define SACENC_LIB_BUILD_DATE ""
+#define SACENC_LIB_BUILD_TIME ""
+#else
+#define SACENC_LIB_BUILD_DATE __DATE__
+#define SACENC_LIB_BUILD_TIME __TIME__
+#endif
+
+#define MAX_MPEGS_BYTES (1 << 14)
+#define MAX_SSC_BYTES (1 << 6)
+
+#define MAX_SPACE_TREE_CHANNELS 2
+#define NUM_KEEP_WINDOWS 3
+
+/* Data Types ****************************************************************/
+typedef struct {
+ MP4SPACEENC_MODE encMode;
+ MP4SPACEENC_BANDS_CONFIG nParamBands;
+ MP4SPACEENC_QUANTMODE quantMode;
+ UCHAR bUseCoarseQuant;
+ UCHAR bLdMode;
+ UCHAR bTimeDomainDmx;
+ UINT sampleRate;
+ UINT frameTimeSlots; /* e.g. 32 when used with HE-AAC */
+ UINT independencyFactor; /* how often should we set the independency flag */
+ INT timeAlignment; /* additional delay for downmix */
+
+} MP4SPACEENC_SETUP, *HANDLE_MP4SPACEENC_SETUP;
+
+struct ENC_CONFIG_SETUP {
+ UCHAR bEncMode_212;
+ UCHAR maxHybridInStaticSlots;
+ LONG maxSamplingrate;
+ INT maxAnalysisLengthTimeSlots;
+ INT maxHybridBands;
+ INT maxQmfBands;
+ INT maxChIn;
+ INT maxFrameTimeSlots;
+ INT maxFrameLength;
+ INT maxChOut;
+ INT maxChTotOut;
+};
+
+struct MP4SPACE_ENCODER {
+ MP4SPACEENC_SETUP user;
+
+ ENC_CONFIG_SETUP setup; /* describe allocated instance */
+
+ HANDLE_FRAMEWINDOW
+ hFrameWindow; /* Windowing, only created+updated, but not used */
+ INT nSamplesValid; /* Input Buffer Handling */
+
+ /* Routing Sensible Switches/Variables */
+ MP4SPACEENC_BANDS_CONFIG nParamBands;
+ UCHAR useTimeDomDownmix;
+
+ /* not Routing Sensible Switches/Varibles - must be contained in Check */
+ MP4SPACEENC_MODE encMode;
+ UCHAR bEncMode_212_only;
+
+ /* not Routing Sensible Switches/Varibles + lower Classes */
+ UCHAR useFrameKeep;
+ UINT independencyFactor;
+ UINT nSampleRate;
+ UCHAR nInputChannels;
+ UCHAR nOutputChannels;
+ UCHAR nFrameTimeSlots; /* e.g. 32 when used with HE-AAC */
+ UCHAR nQmfBands;
+ UCHAR nHybridBands;
+ UINT nFrameLength; /* number of output waveform samples/channel/frame */
+
+ /* not Routing Sensible Switches/Varibles + lower Classes, secondary computed
+ */
+ INT nSamplesNext;
+ INT nAnalysisLengthTimeSlots;
+ INT nAnalysisLookaheadTimeSlots;
+ INT nUpdateHybridPositionTimeSlots;
+ INT *pnOutputBits;
+ INT nInputDelay;
+ INT nOutputBufferDelay;
+ INT nSurroundAnalysisBufferDelay;
+ INT nBitstreamDelayBuffer;
+ INT nBitstreamBufferRead;
+ INT nBitstreamBufferWrite;
+ INT nDiscardOutFrames;
+ INT avoid_keep;
+
+ /* not Routing Sensible Switches/Varibles -> moved to lower Classes */
+ UCHAR useCoarseQuantCld; /* Only Used in SpaceTreeSetup */
+ UCHAR useCoarseQuantIcc; /* Only Used in SpaceTreeSetup */
+ UCHAR useCoarseQuantCpc; /* Only Used in SpaceTreeSetup */
+ UCHAR useCoarseQuantArbDmx; /* ArbitraryDmx,... not available yet */
+ MP4SPACEENC_QUANTMODE
+ quantMode; /* Used for quanitzation and in bitstream writer */
+ INT coreCoderDelay; /* Used in delay compensation */
+ INT timeAlignment; /* Used in delay compensation */
+
+ /* Local Processing Variables */
+ INT independencyCount;
+ INT independencyFlag;
+ INT **ppTrCurrPos; /* belongs somehow to Onset Detection */
+ INT trPrevPos[2 * MAX_NUM_TRANS]; /* belongs somehow to Onset Detection */
+
+ FRAMEWIN_LIST frameWinList;
+ SPATIALFRAME saveFrame;
+
+ /* Module-Handles */
+ SPACE_TREE_SETUP spaceTreeSetup;
+ MPEG4SPACEENC_SSCBUF sscBuf;
+ FIXP_WIN *pFrameWindowAna__FDK[MAX_NUM_PARAMS];
+ HANDLE_QMF_FILTER_BANK *phQmfFiltIn__FDK;
+ HANDLE_DC_FILTER phDCFilterSigIn[SACENC_MAX_INPUT_CHANNELS];
+ HANDLE_ONSET_DETECT phOnset[SACENC_MAX_INPUT_CHANNELS];
+ HANDLE_SPACE_TREE hSpaceTree;
+ HANDLE_BSF_INSTANCE hBitstreamFormatter;
+ HANDLE_STATIC_GAIN_CONFIG hStaticGainConfig;
+ HANDLE_STATIC_GAIN hStaticGain;
+ HANDLE_DELAY hDelay;
+
+ /* enhanced time domain downmix (for stereo input) */
+ HANDLE_ENHANCED_TIME_DOMAIN_DMX hEnhancedTimeDmx;
+
+ /* Data Buffers */
+ INT_PCM **ppTimeSigIn__FDK;
+ INT_PCM **ppTimeSigDelayIn__FDK;
+ INT_PCM **ppTimeSigOut__FDK;
+ FIXP_DPK ***pppHybridIn__FDK;
+ FIXP_DPK ***pppHybridInStatic__FDK;
+ FIXP_DPK ***pppProcDataIn__FDK;
+ INT_PCM *pOutputDelayBuffer__FDK;
+
+ UCHAR **ppBitstreamDelayBuffer;
+
+ UCHAR *pParameterBand2HybridBandOffset;
+ INT staticGainScale;
+
+ INT *pEncoderInputChScale;
+ INT *staticTimeDomainDmxInScale;
+};
+
+/* Constants *****************************************************************/
+static const UCHAR pValidBands_Ld[8] = {4, 5, 7, 9, 12, 15, 23, 40};
+
+static const UCHAR qmf2qmf[] = /* Bypass the HybridAnylyis/Synthesis*/
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
+ 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127};
+
+/* Function / Class Declarations *********************************************/
+static FDK_SACENC_ERROR mp4SpaceEnc_create(
+ HANDLE_MP4SPACE_ENCODER *phMp4SpaceEnc);
+
+static FDK_SACENC_ERROR FillSpatialSpecificConfig(
+ const HANDLE_MP4SPACE_ENCODER hEnc, SPATIALSPECIFICCONFIG *const hSsc);
+
+static FDK_SACENC_ERROR mp4SpaceEnc_FillSpaceTreeSetup(
+ const HANDLE_MP4SPACE_ENCODER hEnc,
+ SPACE_TREE_SETUP *const hSpaceTreeSetup);
+
+static FDK_SACENC_ERROR mp4SpaceEnc_InitDelayCompensation(
+ HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc, const INT coreCoderDelay);
+
+static FDK_SACENC_ERROR mp4SpaceEnc_InitDefault(
+ HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc);
+
+static DECORRCONFIG mp4SpaceEnc_GetDecorrConfig(const MP4SPACEENC_MODE encMode);
+
+static FDK_SACENC_ERROR mp4SpaceEnc_InitNumParamBands(
+ HANDLE_MP4SPACE_ENCODER hEnc, const MP4SPACEENC_BANDS_CONFIG nParamBands);
+
+/* Function / Class Definition ***********************************************/
+static UINT mp4SpaceEnc_GetNumQmfBands(const UINT nSampleRate) {
+ UINT nQmfBands = 0;
+
+ if (nSampleRate < 27713)
+ nQmfBands = 32;
+ else if (nSampleRate < 55426)
+ nQmfBands = 64;
+
+ return nQmfBands;
+}
+
+static UINT updateQmfFlags(const UINT flags, const INT keepStates) {
+ UINT qmfFlags = flags;
+
+ qmfFlags = (qmfFlags & (~(UINT)QMF_FLAG_LP));
+ qmfFlags = (qmfFlags | QMF_FLAG_MPSLDFB);
+ qmfFlags = (keepStates) ? (qmfFlags | QMF_FLAG_KEEP_STATES)
+ : (qmfFlags & (~(UINT)QMF_FLAG_KEEP_STATES));
+
+ return qmfFlags;
+}
+
+static INT freq2HybridBand(const UINT nFrequency, const UINT nSampleRate,
+ const UINT nQmfBands) {
+ /*
+ nQmfSlotWidth = (nSampleRate/2) / nQmfBands;
+ nQmfBand = nFrequency / nQmfSlotWidth;
+ */
+ int nHybridBand = -1;
+ int scale = 0;
+ const FIXP_DBL temp = fDivNorm((FIXP_DBL)(2 * nFrequency * nQmfBands),
+ (FIXP_DBL)nSampleRate, &scale);
+ const int nQmfBand = scaleValue(temp, scale - (DFRACT_BITS - 1));
+
+ if ((nQmfBand > -1) && (nQmfBand < (int)nQmfBands)) {
+ nHybridBand = qmf2qmf[nQmfBand];
+ }
+
+ return nHybridBand;
+}
+
+/*
+ * Examine buffer descriptor regarding choosen type.
+ *
+ * \param pBufDesc Pointer to buffer descriptor
+ * \param type Buffer type to look for.
+
+ * \return - Buffer descriptor index.
+ * -1, if there is no entry available.
+ */
+static INT getBufDescIdx(const FDK_bufDescr *pBufDesc, const UINT type) {
+ INT i, idx = -1;
+
+ for (i = 0; i < (int)pBufDesc->numBufs; i++) {
+ if (pBufDesc->pBufType[i] == type) {
+ idx = i;
+ break;
+ }
+ }
+ return idx;
+}
+
+FDK_SACENC_ERROR FDK_sacenc_open(HANDLE_MP4SPACE_ENCODER *phMp4SpaceEnc) {
+ return mp4SpaceEnc_create(phMp4SpaceEnc);
+}
+
+static FDK_SACENC_ERROR mp4SpaceEnc_create(
+ HANDLE_MP4SPACE_ENCODER *phMp4SpaceEnc) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+ HANDLE_MP4SPACE_ENCODER hEnc = NULL;
+ ENC_CONFIG_SETUP setup;
+
+ if (NULL == phMp4SpaceEnc) {
+ error = SACENC_INVALID_HANDLE;
+ } else {
+ int i, ch;
+ FDKmemclear(&setup, sizeof(ENC_CONFIG_SETUP));
+
+ /* Allocate Encoder Instance */
+ FDK_ALLOCATE_MEMORY_1D(hEnc, 1, struct MP4SPACE_ENCODER);
+
+ /* Clear everything, also pointers. */
+ if (NULL != hEnc) {
+ FDKmemclear(hEnc, sizeof(struct MP4SPACE_ENCODER));
+ }
+
+ setup.maxSamplingrate = 48000;
+ setup.maxFrameTimeSlots = 16;
+
+ setup.maxAnalysisLengthTimeSlots = 3 * setup.maxFrameTimeSlots;
+ setup.maxQmfBands = mp4SpaceEnc_GetNumQmfBands(setup.maxSamplingrate);
+ ;
+ setup.maxHybridBands = setup.maxQmfBands;
+ setup.maxFrameLength = setup.maxQmfBands * setup.maxFrameTimeSlots;
+
+ setup.maxChIn = 2;
+ setup.maxChOut = 1;
+ setup.maxChTotOut = setup.maxChOut;
+ setup.bEncMode_212 = 1;
+ setup.maxHybridInStaticSlots = 24;
+
+ /* Open Static Gain*/
+ if (SACENC_OK !=
+ (error = fdk_sacenc_staticGain_OpenConfig(&hEnc->hStaticGainConfig))) {
+ goto bail;
+ }
+
+ /* enhanced time domain downmix (for stereo input) */
+ if (SACENC_OK != (error = fdk_sacenc_open_enhancedTimeDomainDmx(
+ &hEnc->hEnhancedTimeDmx, setup.maxFrameLength))) {
+ goto bail;
+ }
+
+ FDK_ALLOCATE_MEMORY_1D(hEnc->pParameterBand2HybridBandOffset,
+ MAX_NUM_PARAM_BANDS, UCHAR);
+
+ /* Create Space Tree first, to get number of in-/output channels */
+ if (SACENC_OK != (error = fdk_sacenc_spaceTree_Open(&hEnc->hSpaceTree))) {
+ goto bail;
+ }
+
+ FDK_ALLOCATE_MEMORY_1D(hEnc->pEncoderInputChScale, setup.maxChIn, INT);
+ FDK_ALLOCATE_MEMORY_1D(hEnc->staticTimeDomainDmxInScale, setup.maxChIn,
+ INT);
+
+ FDK_ALLOCATE_MEMORY_1D(hEnc->phQmfFiltIn__FDK, setup.maxChIn,
+ HANDLE_QMF_FILTER_BANK);
+
+ /* Allocate Analysis Filterbank Structs */
+ for (ch = 0; ch < setup.maxChIn; ch++) {
+ FDK_ALLOCATE_MEMORY_1D_INT(hEnc->phQmfFiltIn__FDK[ch], 1,
+ struct QMF_FILTER_BANK, SECT_DATA_L2)
+ FDK_ALLOCATE_MEMORY_1D_INT(hEnc->phQmfFiltIn__FDK[ch]->FilterStates,
+ 2 * 5 * setup.maxQmfBands, FIXP_QAS,
+ SECT_DATA_L2)
+ }
+
+ /* Allocate Synthesis Filterbank Structs for arbitrary downmix */
+
+ /* Allocate DC Filter Struct for normal signal input */
+ for (ch = 0; ch < setup.maxChIn; ch++) {
+ if (SACENC_OK !=
+ (error = fdk_sacenc_createDCFilter(&hEnc->phDCFilterSigIn[ch]))) {
+ goto bail;
+ }
+ }
+
+ /* Open Onset Detection */
+ for (ch = 0; ch < setup.maxChIn; ch++) {
+ if (SACENC_OK != (error = fdk_sacenc_onsetDetect_Open(
+ &hEnc->phOnset[ch], setup.maxFrameTimeSlots))) {
+ goto bail;
+ }
+ }
+
+ FDK_ALLOCATE_MEMORY_2D(hEnc->ppTrCurrPos, setup.maxChIn, MAX_NUM_TRANS,
+ INT);
+
+ /* Create Windowing */
+ if (SACENC_OK !=
+ (error = fdk_sacenc_frameWindow_Create(&hEnc->hFrameWindow))) {
+ goto bail;
+ }
+
+ /* Open static gain */
+ if (SACENC_OK != (error = fdk_sacenc_staticGain_Open(&hEnc->hStaticGain))) {
+ goto bail;
+ }
+
+ /* create bitstream encoder */
+ if (SACENC_OK != (error = fdk_sacenc_createSpatialBitstreamEncoder(
+ &hEnc->hBitstreamFormatter))) {
+ goto bail;
+ }
+
+ FDK_ALLOCATE_MEMORY_1D(hEnc->sscBuf.pSsc, MAX_SSC_BYTES, UCHAR);
+
+ {
+ FDK_ALLOCATE_MEMORY_2D(hEnc->ppTimeSigIn__FDK, setup.maxChIn,
+ setup.maxFrameLength + MAX_DELAY_SURROUND_ANALYSIS,
+ INT_PCM);
+ }
+ FDK_ALLOCATE_MEMORY_2D(hEnc->ppTimeSigDelayIn__FDK, setup.maxChIn,
+ MAX_DELAY_SURROUND_ANALYSIS, INT_PCM);
+
+ /* Create new buffers for several signals (including arbitrary downmix) */
+ if (setup.bEncMode_212 == 0) {
+ /* pOutputDelayBuffer__FDK buffer is not needed for SACENC_212 mode */
+ FDK_ALLOCATE_MEMORY_1D(
+ hEnc->pOutputDelayBuffer__FDK,
+ (setup.maxFrameLength + MAX_DELAY_OUTPUT) * setup.maxChOut, INT_PCM);
+ }
+
+ /* allocate buffers */
+ if (setup.bEncMode_212 == 0) {
+ /* ppTimeSigOut__FDK buffer is not needed for SACENC_212 mode */
+ FDK_ALLOCATE_MEMORY_2D(hEnc->ppTimeSigOut__FDK, setup.maxChTotOut,
+ setup.maxFrameLength, INT_PCM);
+ }
+
+ if (setup.bEncMode_212 == 1) {
+ /* pppHybridIn__FDK buffer can be reduced by maxFrameTimeSlots/2 slots for
+ * SACENC_212 mode */
+ FDK_ALLOCATE_MEMORY_3D(
+ hEnc->pppHybridIn__FDK, setup.maxChIn,
+ setup.maxAnalysisLengthTimeSlots - (setup.maxFrameTimeSlots >> 1),
+ setup.maxHybridBands, FIXP_DPK);
+ FDK_ALLOCATE_MEMORY_3D(hEnc->pppHybridInStatic__FDK, setup.maxChIn,
+ setup.maxHybridInStaticSlots, setup.maxHybridBands,
+ FIXP_DPK);
+ } else {
+ FDK_ALLOCATE_MEMORY_3D(hEnc->pppHybridIn__FDK, setup.maxChIn,
+ setup.maxAnalysisLengthTimeSlots,
+ setup.maxHybridBands, FIXP_DPK);
+ }
+
+ if (setup.bEncMode_212 == 0) {
+ /* pppProcDataIn__FDK buffer is not needed for SACENC_212 mode */
+ FDK_ALLOCATE_MEMORY_3D(hEnc->pppProcDataIn__FDK, MAX_SPACE_TREE_CHANNELS,
+ setup.maxAnalysisLengthTimeSlots,
+ setup.maxHybridBands, FIXP_DPK);
+ }
+ for (i = 0; i < MAX_NUM_PARAMS; i++) {
+ FDK_ALLOCATE_MEMORY_1D(hEnc->pFrameWindowAna__FDK[i],
+ setup.maxAnalysisLengthTimeSlots, FIXP_WIN);
+ } /* for i */
+
+ if (SACENC_OK != (error = fdk_sacenc_delay_Open(&hEnc->hDelay))) {
+ goto bail;
+ }
+
+ if (setup.bEncMode_212 == 0) {
+ /* ppBitstreamDelayBuffer buffer is not needed for SACENC_212 mode */
+ FDK_ALLOCATE_MEMORY_2D(hEnc->ppBitstreamDelayBuffer, MAX_BITSTREAM_DELAY,
+ MAX_MPEGS_BYTES, UCHAR);
+ }
+ FDK_ALLOCATE_MEMORY_1D(hEnc->pnOutputBits, MAX_BITSTREAM_DELAY, INT);
+
+ hEnc->setup = setup; /* save configuration used while encoder allocation. */
+ mp4SpaceEnc_InitDefault(hEnc);
+
+ if (NULL != phMp4SpaceEnc) {
+ *phMp4SpaceEnc = hEnc; /* return encoder handle */
+ }
+
+ } /* valid handle */
+
+ return error;
+
+bail:
+ if (NULL != hEnc) {
+ hEnc->setup = setup;
+ FDK_sacenc_close(&hEnc);
+ }
+ return ((SACENC_OK == error) ? SACENC_MEMORY_ERROR : error);
+}
+
+static FDK_SACENC_ERROR mp4SpaceEnc_InitDefault(HANDLE_MP4SPACE_ENCODER hEnc) {
+ FDK_SACENC_ERROR err = SACENC_OK;
+
+ /* Get default static gain configuration. */
+ if (SACENC_OK != (err = fdk_sacenc_staticGain_InitDefaultConfig(
+ hEnc->hStaticGainConfig))) {
+ goto bail;
+ }
+
+bail:
+ return err;
+}
+
+static FDK_SACENC_ERROR FDK_sacenc_configure(
+ HANDLE_MP4SPACE_ENCODER hEnc, const HANDLE_MP4SPACEENC_SETUP hSetup) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ hEnc->nSampleRate = hSetup->sampleRate;
+ hEnc->encMode = hSetup->encMode;
+ hEnc->nQmfBands = mp4SpaceEnc_GetNumQmfBands(hEnc->nSampleRate);
+
+ /* Make sure that we have set time domain downmix for 212 */
+ if (hSetup->encMode == SACENC_212 && hSetup->bTimeDomainDmx == 0) {
+ error = SACENC_INVALID_CONFIG;
+ } else {
+ hEnc->useTimeDomDownmix = hSetup->bTimeDomainDmx;
+ }
+
+ hEnc->timeAlignment = hSetup->timeAlignment;
+ hEnc->quantMode = hSetup->quantMode;
+
+ hEnc->useCoarseQuantCld = hSetup->bUseCoarseQuant;
+ hEnc->useCoarseQuantCpc = hSetup->bUseCoarseQuant;
+ hEnc->useFrameKeep = (hSetup->bLdMode == 2);
+ hEnc->useCoarseQuantIcc = 0; /* not available */
+ hEnc->useCoarseQuantArbDmx = 0; /* not available for user right now */
+ hEnc->independencyFactor = hSetup->independencyFactor;
+ hEnc->independencyCount = 0;
+ hEnc->independencyFlag = 1;
+
+ /* set number of Hybrid bands */
+ hEnc->nHybridBands = hEnc->nQmfBands;
+ hEnc->nFrameTimeSlots = hSetup->frameTimeSlots;
+ mp4SpaceEnc_InitNumParamBands(hEnc, hSetup->nParamBands);
+
+ return error;
+}
+
+FDK_SACENC_ERROR FDK_sacenc_init(HANDLE_MP4SPACE_ENCODER hEnc,
+ const INT dmxDelay) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ /* Sanity Checks */
+ if (NULL == hEnc) {
+ error = SACENC_INVALID_HANDLE;
+ } else {
+ const int initStatesFlag = 1;
+
+ int ch; /* loop counter */
+ int nChInArbDmx;
+
+ if (SACENC_OK != (error = FDK_sacenc_configure(hEnc, &hEnc->user))) {
+ goto bail;
+ }
+
+ hEnc->bEncMode_212_only = hEnc->setup.bEncMode_212;
+
+ /* Slots per Frame and Frame Length */
+ if (hEnc->nFrameTimeSlots < 1) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+ hEnc->nFrameLength = hEnc->nQmfBands * hEnc->nFrameTimeSlots;
+
+ if (hEnc->useFrameKeep == 1) {
+ hEnc->nAnalysisLengthTimeSlots = 3 * hEnc->nFrameTimeSlots;
+ hEnc->nUpdateHybridPositionTimeSlots = hEnc->nFrameTimeSlots;
+ } else {
+ hEnc->nAnalysisLengthTimeSlots = 2 * hEnc->nFrameTimeSlots;
+ hEnc->nUpdateHybridPositionTimeSlots = 0;
+ }
+
+ {
+ hEnc->nAnalysisLookaheadTimeSlots =
+ hEnc->nAnalysisLengthTimeSlots - 3 * hEnc->nFrameTimeSlots / 2;
+ }
+
+ /* init parameterBand2hybridBandOffset table */
+ fdk_sacenc_calcParameterBand2HybridBandOffset(
+ (BOX_SUBBAND_CONFIG)hEnc->nParamBands, hEnc->nHybridBands,
+ hEnc->pParameterBand2HybridBandOffset);
+
+ /* Fill Setup structure for Space Tree */
+ if (SACENC_OK !=
+ (error = mp4SpaceEnc_FillSpaceTreeSetup(hEnc, &hEnc->spaceTreeSetup))) {
+ goto bail;
+ }
+
+ /* Init space tree configuration */
+ if (SACENC_OK !=
+ (error = fdk_sacenc_spaceTree_Init(
+ hEnc->hSpaceTree, &hEnc->spaceTreeSetup,
+ hEnc->pParameterBand2HybridBandOffset, hEnc->useFrameKeep))) {
+ goto bail;
+ }
+
+ /* Get space tree description and resulting number of input/output channels
+ */
+ {
+ SPACE_TREE_DESCRIPTION spaceTreeDescription;
+
+ if (SACENC_OK != (error = fdk_sacenc_spaceTree_GetDescription(
+ hEnc->hSpaceTree, &spaceTreeDescription))) {
+ goto bail;
+ }
+
+ hEnc->nInputChannels =
+ spaceTreeDescription.nOutChannels; /* space tree description
+ describes decoder
+ configuration */
+ hEnc->nOutputChannels =
+ spaceTreeDescription.nInChannels; /* space tree description
+ describes decoder
+ configuration */
+ }
+
+ nChInArbDmx = 0;
+
+ /* INITIALIZATION */
+ for (ch = 0; ch < hEnc->nInputChannels; ch++) {
+ /* scaling in analysis qmf filterbank (7) */
+ hEnc->pEncoderInputChScale[ch] = 7;
+
+ {
+ /* additional scaling in qmf prototype filter for low delay */
+ hEnc->pEncoderInputChScale[ch] += 1;
+ }
+
+ { hEnc->pEncoderInputChScale[ch] += DC_FILTER_SF; }
+ } /* nInputChannels */
+
+ /* Init analysis filterbank */
+ for (ch = 0; ch < hEnc->nInputChannels; ch++) {
+ hEnc->phQmfFiltIn__FDK[ch]->flags =
+ updateQmfFlags(hEnc->phQmfFiltIn__FDK[ch]->flags, !initStatesFlag);
+
+ if (0 != qmfInitAnalysisFilterBank(
+ hEnc->phQmfFiltIn__FDK[ch],
+ (FIXP_QAS *)hEnc->phQmfFiltIn__FDK[ch]->FilterStates, 1,
+ hEnc->nQmfBands, hEnc->nQmfBands, hEnc->nQmfBands,
+ hEnc->phQmfFiltIn__FDK[ch]->flags)) {
+ error = SACENC_INIT_ERROR;
+ goto bail;
+ }
+ }
+
+ /* Initialize DC Filter. */
+ {
+ for (ch = 0; ch < hEnc->nInputChannels; ch++) {
+ if (SACENC_OK != (error = fdk_sacenc_initDCFilter(
+ hEnc->phDCFilterSigIn[ch], hEnc->nSampleRate))) {
+ goto bail;
+ }
+ }
+ }
+
+ /* Init onset detect. */
+ {
+ /* init onset detect configuration struct */
+ ONSET_DETECT_CONFIG onsetDetectConfig;
+ onsetDetectConfig.maxTimeSlots = hEnc->nFrameTimeSlots;
+ onsetDetectConfig.lowerBoundOnsetDetection =
+ freq2HybridBand(1725, hEnc->nSampleRate, hEnc->nQmfBands);
+ onsetDetectConfig.upperBoundOnsetDetection = hEnc->nHybridBands;
+
+ for (ch = 0; ch < hEnc->nInputChannels; ch++) {
+ if (SACENC_OK != (error = fdk_sacenc_onsetDetect_Init(
+ hEnc->phOnset[ch], &onsetDetectConfig, 1))) {
+ goto bail;
+ }
+ }
+ }
+
+ {
+ /* init windowing */
+ FRAMEWINDOW_CONFIG framewindowConfig;
+ framewindowConfig.nTimeSlotsMax = hEnc->nFrameTimeSlots;
+ framewindowConfig.bFrameKeep = hEnc->useFrameKeep;
+
+ if (SACENC_OK != (error = fdk_sacenc_frameWindow_Init(
+ hEnc->hFrameWindow, &framewindowConfig))) {
+ goto bail;
+ }
+ }
+
+ /* Set encoder mode for static gain initialization. */
+ if (SACENC_OK != (error = fdk_sacenc_staticGain_SetEncMode(
+ hEnc->hStaticGainConfig, hEnc->encMode))) {
+ goto bail;
+ }
+
+ /* Init static gain. */
+ if (SACENC_OK != (error = fdk_sacenc_staticGain_Init(
+ hEnc->hStaticGain, hEnc->hStaticGainConfig,
+ &(hEnc->staticGainScale)))) {
+ goto bail;
+ }
+
+ for (ch = 0; ch < hEnc->nInputChannels; ch++) {
+ hEnc->pEncoderInputChScale[ch] += hEnc->staticGainScale;
+ }
+
+ /* enhanced downmix for stereo input*/
+ if (hEnc->useTimeDomDownmix != 0) {
+ if (SACENC_OK != (error = fdk_sacenc_init_enhancedTimeDomainDmx(
+ hEnc->hEnhancedTimeDmx,
+ fdk_sacenc_getPreGainPtrFDK(hEnc->hStaticGain),
+ hEnc->staticGainScale,
+ fdk_sacenc_getPostGainFDK(hEnc->hStaticGain),
+ hEnc->staticGainScale, hEnc->nFrameLength))) {
+ goto bail;
+ }
+ }
+
+ /* Create config structure for bitstream formatter including arbitrary
+ * downmix residual */
+ if (SACENC_OK != (error = fdk_sacenc_initSpatialBitstreamEncoder(
+ hEnc->hBitstreamFormatter))) {
+ goto bail;
+ }
+
+ if (SACENC_OK != (error = FillSpatialSpecificConfig(
+ hEnc, fdk_sacenc_getSpatialSpecificConfig(
+ hEnc->hBitstreamFormatter)))) {
+ goto bail;
+ }
+
+ if (SACENC_OK !=
+ (error = fdk_sacenc_writeSpatialSpecificConfig(
+ fdk_sacenc_getSpatialSpecificConfig(hEnc->hBitstreamFormatter),
+ hEnc->sscBuf.pSsc, MAX_SSC_BYTES, &hEnc->sscBuf.nSscSizeBits))) {
+ goto bail;
+ }
+
+ /* init delay compensation with dmx core coder delay; if no core coder is
+ * used, many other buffers are initialized nevertheless */
+ if (SACENC_OK !=
+ (error = mp4SpaceEnc_InitDelayCompensation(hEnc, dmxDelay))) {
+ goto bail;
+ }
+
+ /* How much input do we need? */
+ hEnc->nSamplesNext =
+ hEnc->nFrameLength * (hEnc->nInputChannels + nChInArbDmx);
+ hEnc->nSamplesValid = 0;
+ } /* valid handle */
+
+bail:
+ return error;
+}
+
+static INT getAnalysisLengthTimeSlots(FIXP_WIN *pFrameWindowAna,
+ INT nTimeSlots) {
+ int i;
+ for (i = nTimeSlots - 1; i >= 0; i--) {
+ if (pFrameWindowAna[i] != (FIXP_WIN)0) {
+ break;
+ }
+ }
+ nTimeSlots = i + 1;
+ return nTimeSlots;
+}
+
+static INT getAnalysisStartTimeSlot(FIXP_WIN *pFrameWindowAna, INT nTimeSlots) {
+ int startTimeSlot = 0;
+ int i;
+ for (i = 0; i < nTimeSlots; i++) {
+ if (pFrameWindowAna[i] != (FIXP_WIN)0) {
+ break;
+ }
+ }
+ startTimeSlot = i;
+ return startTimeSlot;
+}
+
+static FDK_SACENC_ERROR __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) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ if ((hEnc == NULL) || (pSamples == NULL) || (pnSamplesFed == NULL)) {
+ error = SACENC_INVALID_HANDLE;
+ } else if (nSamples == 0) {
+ error = SACENC_INVALID_CONFIG; /* Flushing not implemented */
+ } else {
+ int ch;
+ const INT nChIn = hEnc->nInputChannels;
+ const INT nChInWithDmx = nChIn;
+ const INT samplesToFeed =
+ FDKmin(nSamples, hEnc->nSamplesNext - hEnc->nSamplesValid);
+ const INT nSamplesPerChannel = samplesToFeed / nChInWithDmx;
+
+ if ((samplesToFeed < 0) || (samplesToFeed % nChInWithDmx != 0) ||
+ (samplesToFeed > nChInWithDmx * (INT)hEnc->nFrameLength)) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+ int i;
+
+ const INT_PCM *pInput__FDK;
+ const INT_PCM *pInput2__FDK;
+
+ { /* no dmx align = default*/
+ pInput__FDK = pSamples;
+ pInput2__FDK = pSamples + (hEnc->nInputDelay * nChInWithDmx);
+ }
+
+ for (i = 0; i < hEnc->nInputChannels; i++) {
+ hEnc->staticTimeDomainDmxInScale[i] = hEnc->staticGainScale;
+ }
+
+ /***** N-channel-input *****/
+ for (ch = 0; ch < nChIn; ch++) {
+ /* Write delayed time signal into time signal buffer */
+ FDKmemcpy(&(hEnc->ppTimeSigIn__FDK[ch][0]),
+ &(hEnc->ppTimeSigDelayIn__FDK[ch][0]),
+ hEnc->nSurroundAnalysisBufferDelay * sizeof(INT_PCM));
+
+ if (isInputInterleaved) {
+ /* Add the new frame de-interleaved. Apply nSurroundAnalysisBufferDelay.
+ */
+ FDKmemcpy_flex(
+ &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nSurroundAnalysisBufferDelay]),
+ 1, pInput__FDK + ch, nChInWithDmx, hEnc->nInputDelay);
+ FDKmemcpy_flex(
+ &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nSurroundAnalysisBufferDelay +
+ hEnc->nInputDelay]),
+ 1, pInput2__FDK + ch, nChInWithDmx,
+ nSamplesPerChannel - hEnc->nInputDelay);
+ } else {
+ /* Input is already deinterleaved, just copy */
+ FDKmemcpy(
+ &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nSurroundAnalysisBufferDelay]),
+ pInput__FDK + ch * inputBufferSizePerChannel,
+ hEnc->nInputDelay * sizeof(INT_PCM));
+ FDKmemcpy(
+ &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nSurroundAnalysisBufferDelay +
+ hEnc->nInputDelay]),
+ pInput2__FDK + ch * inputBufferSizePerChannel,
+ (nSamplesPerChannel - hEnc->nInputDelay) * sizeof(INT_PCM));
+ }
+
+ /* Update time signal delay buffer */
+ FDKmemcpy(&(hEnc->ppTimeSigDelayIn__FDK[ch][0]),
+ &(hEnc->ppTimeSigIn__FDK[ch][hEnc->nFrameLength]),
+ hEnc->nSurroundAnalysisBufferDelay * sizeof(INT_PCM));
+ } /* for ch */
+
+ /***** No Arbitrary Downmix *****/
+ /* "Crude TD Dmx": Time DomainDownmix + NO Arbitrary Downmix, Delay Added at
+ * pOutputBuffer */
+ if ((hEnc->useTimeDomDownmix > 0)) {
+ if ((hEnc->useTimeDomDownmix == 1) || (hEnc->nInputChannels != 2)) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ } else {
+ /* enhanced time domain downmix (for stereo input) */
+ if (hEnc->encMode == SACENC_212) {
+ if (pOutputSamples == NULL) {
+ error = SACENC_INVALID_HANDLE;
+ goto bail;
+ }
+
+ fdk_sacenc_apply_enhancedTimeDomainDmx(
+ hEnc->hEnhancedTimeDmx, hEnc->ppTimeSigIn__FDK, pOutputSamples,
+ hEnc->nSurroundAnalysisBufferDelay);
+ } else {
+ if (&hEnc->ppTimeSigOut__FDK[0][0] == NULL) {
+ error = SACENC_INVALID_HANDLE;
+ goto bail;
+ }
+
+ fdk_sacenc_apply_enhancedTimeDomainDmx(
+ hEnc->hEnhancedTimeDmx, hEnc->ppTimeSigIn__FDK,
+ &hEnc->ppTimeSigOut__FDK[0][0],
+ hEnc->nSurroundAnalysisBufferDelay);
+ }
+ }
+ }
+
+ /* update number of samples still to process */
+ hEnc->nSamplesValid += samplesToFeed;
+
+ /*return number of fed samples */
+ *pnSamplesFed = samplesToFeed;
+ }
+bail:
+ return error;
+}
+
+FDK_SACENC_ERROR FDK_sacenc_encode(const HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,
+ const FDK_bufDescr *inBufDesc,
+ const FDK_bufDescr *outBufDesc,
+ const SACENC_InArgs *inargs,
+ SACENC_OutArgs *outargs) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ const INT_PCM *pInputSamples =
+ (const INT_PCM *)inBufDesc->ppBase[getBufDescIdx(
+ inBufDesc, (FDK_BUF_TYPE_INPUT | FDK_BUF_TYPE_PCM_DATA))];
+
+ INT_PCM *const pOutputSamples = (INT_PCM *)outBufDesc->ppBase[getBufDescIdx(
+ outBufDesc, (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_PCM_DATA))];
+
+ const int nOutputSamplesBufferSize =
+ outBufDesc->pBufSize[getBufDescIdx(
+ outBufDesc, (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_PCM_DATA))] /
+ outBufDesc->pEleSize[getBufDescIdx(
+ outBufDesc, (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_PCM_DATA))];
+
+ if ((hMp4SpaceEnc == NULL) || (pInputSamples == NULL)) {
+ error = SACENC_INVALID_HANDLE;
+ } else {
+ int nOutputSamples;
+ int i, ch, ps, winCnt, ts, slot;
+ INT currTransPos = -1;
+ SPATIALFRAME *pFrameData = NULL;
+
+ /* Improve Code Readability */
+ const int nChIn = hMp4SpaceEnc->nInputChannels;
+ const int nChInWithDmx = nChIn;
+ const int nChOut = hMp4SpaceEnc->nOutputChannels;
+ const int nSamplesPerChannel = inargs->nInputSamples / nChInWithDmx;
+ const int nOutputSamplesMax = nSamplesPerChannel * nChOut;
+ const int nFrameTimeSlots = hMp4SpaceEnc->nFrameTimeSlots;
+
+ INT encoderInputChScale[SACENC_MAX_INPUT_CHANNELS];
+ INT nFrameTimeSlotsReduction = 0;
+
+ if (hMp4SpaceEnc->encMode == SACENC_212) {
+ nFrameTimeSlotsReduction = hMp4SpaceEnc->nFrameTimeSlots >> 1;
+ }
+
+ for (i = 0; i < nChIn; i++)
+ encoderInputChScale[i] = hMp4SpaceEnc->pEncoderInputChScale[i];
+
+ /* Sanity Check */
+ if ((0 != inargs->nInputSamples % nChInWithDmx)) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+
+ /*
+ * Get Frame Data Handle.
+ */
+
+ /* get bitstream handle (for storage of cld's, icc's and so on)
+ * get spatialframe 2 frames in the future; NOTE: this is necessary to
+ * synchronise spatial data and audio data */
+ if (NULL == (pFrameData = fdk_sacenc_getSpatialFrame(
+ hMp4SpaceEnc->hBitstreamFormatter, WRITE_SPATIALFRAME))) {
+ error = SACENC_INVALID_HANDLE;
+ goto bail;
+ }
+
+ /* Independent Frames Counters*/
+ if (hMp4SpaceEnc->nDiscardOutFrames >
+ 0) { /* Independent Frames if they should be discarded, Reset Counter*/
+ hMp4SpaceEnc->independencyCount =
+ 0; /* Reset the counter, first valid frame is an independent one*/
+ hMp4SpaceEnc->independencyFlag = 1;
+ } else { /*hMp4SpaceEnc->nDiscardOutFrames == 0*/
+ hMp4SpaceEnc->independencyFlag =
+ (hMp4SpaceEnc->independencyCount == 0) ? 1 : 0;
+ if (hMp4SpaceEnc->independencyFactor > 0) {
+ hMp4SpaceEnc->independencyCount++;
+ hMp4SpaceEnc->independencyCount =
+ hMp4SpaceEnc->independencyCount %
+ ((int)hMp4SpaceEnc->independencyFactor);
+ } else { /* independencyFactor == 0 */
+ hMp4SpaceEnc->independencyCount = -1;
+ }
+ }
+
+ /*
+ * Time signal preprocessing:
+ * - Feed input buffer
+ * - Prescale time signal
+ * - Apply DC filter on input signal
+ */
+
+ /* Feed, Deinterleave, Pre-Scale the input time signals */
+ if (SACENC_OK !=
+ (error = __FeedDeinterPreScale(
+ hMp4SpaceEnc, pInputSamples, pOutputSamples, inargs->nInputSamples,
+ inargs->isInputInterleaved, inargs->inputBufferSizePerChannel,
+ &outargs->nSamplesConsumed))) {
+ goto bail;
+ }
+
+ if (hMp4SpaceEnc->nSamplesNext != hMp4SpaceEnc->nSamplesValid) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+
+ if (hMp4SpaceEnc->encMode == SACENC_212 &&
+ hMp4SpaceEnc->bEncMode_212_only) {
+ for (ch = 0; ch < nChIn; ch++) {
+ for (slot = 0; slot < nFrameTimeSlots; slot++) {
+ setCplxVec(
+ hMp4SpaceEnc->pppHybridIn__FDK
+ [ch][hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
+ nFrameTimeSlots - nFrameTimeSlotsReduction + slot],
+ (FIXP_DBL)0, hMp4SpaceEnc->nHybridBands);
+ }
+ }
+ }
+
+ /*
+ * Time / Frequency:
+ * - T/F audio input channels
+ * - T/F arbitrary downmix input channels
+ */
+ for (ch = 0; ch < nChIn; ch++) {
+ C_AALLOC_SCRATCH_START(pQmfInReal, FIXP_DBL, MAX_QMF_BANDS)
+ C_AALLOC_SCRATCH_START(pQmfInImag, FIXP_DBL, MAX_QMF_BANDS)
+ FIXP_GAIN *pPreGain =
+ fdk_sacenc_getPreGainPtrFDK(hMp4SpaceEnc->hStaticGain);
+
+ for (ts = 0; ts < nFrameTimeSlots; ts++) {
+ FIXP_DBL *pSpecReal;
+ FIXP_DBL *pSpecImag;
+
+ INT_PCM *pTimeIn =
+ &hMp4SpaceEnc->ppTimeSigIn__FDK[ch][(ts * hMp4SpaceEnc->nQmfBands)];
+
+ {
+ /* Apply DC filter on input channels */
+ if (SACENC_OK != (error = fdk_sacenc_applyDCFilter(
+ hMp4SpaceEnc->phDCFilterSigIn[ch], pTimeIn,
+ pTimeIn, hMp4SpaceEnc->nQmfBands))) {
+ goto bail;
+ }
+ }
+
+ /* QMF filterbank */
+ C_ALLOC_SCRATCH_START(pWorkBuffer, FIXP_DBL, (MAX_QMF_BANDS << 1));
+
+ qmfAnalysisFilteringSlot(hMp4SpaceEnc->phQmfFiltIn__FDK[ch], pQmfInReal,
+ pQmfInImag, pTimeIn, 1, pWorkBuffer);
+
+ C_ALLOC_SCRATCH_END(pWorkBuffer, FIXP_DBL, (MAX_QMF_BANDS << 1));
+
+ pSpecReal = pQmfInReal;
+ pSpecImag = pQmfInImag;
+
+ /* Apply pre-scale after filterbank */
+ if (MAXVAL_GAIN != pPreGain[ch]) {
+ for (i = 0; i < hMp4SpaceEnc->nHybridBands; i++) {
+ hMp4SpaceEnc
+ ->pppHybridIn__FDK[ch]
+ [hMp4SpaceEnc->nAnalysisLookaheadTimeSlots +
+ ts][i]
+ .v.re = fMult(pSpecReal[i], pPreGain[ch]);
+ hMp4SpaceEnc
+ ->pppHybridIn__FDK[ch]
+ [hMp4SpaceEnc->nAnalysisLookaheadTimeSlots +
+ ts][i]
+ .v.im = fMult(pSpecImag[i], pPreGain[ch]);
+ }
+ } else {
+ for (i = 0; i < hMp4SpaceEnc->nHybridBands; i++) {
+ hMp4SpaceEnc
+ ->pppHybridIn__FDK[ch]
+ [hMp4SpaceEnc->nAnalysisLookaheadTimeSlots +
+ ts][i]
+ .v.re = pSpecReal[i];
+ hMp4SpaceEnc
+ ->pppHybridIn__FDK[ch]
+ [hMp4SpaceEnc->nAnalysisLookaheadTimeSlots +
+ ts][i]
+ .v.im = pSpecImag[i];
+ }
+ }
+ } /* ts */
+ C_AALLOC_SCRATCH_END(pQmfInImag, FIXP_DBL, MAX_QMF_BANDS)
+ C_AALLOC_SCRATCH_END(pQmfInReal, FIXP_DBL, MAX_QMF_BANDS)
+
+ if (SACENC_OK != error) {
+ goto bail;
+ }
+ } /* ch */
+
+ if (hMp4SpaceEnc->encMode == SACENC_212 &&
+ hMp4SpaceEnc->bEncMode_212_only) {
+ for (ch = 0; ch < nChIn; ch++) {
+ for (slot = 0;
+ slot < (int)(hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
+ nFrameTimeSlots - nFrameTimeSlotsReduction);
+ slot++) {
+ copyCplxVec(hMp4SpaceEnc->pppHybridIn__FDK[ch][slot],
+ hMp4SpaceEnc->pppHybridInStatic__FDK[ch][slot],
+ hMp4SpaceEnc->nHybridBands);
+ }
+ }
+ for (ch = 0; ch < nChIn; ch++) {
+ for (slot = 0;
+ slot < (int)(hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
+ nFrameTimeSlots - nFrameTimeSlotsReduction);
+ slot++) {
+ copyCplxVec(
+ hMp4SpaceEnc->pppHybridInStatic__FDK[ch][slot],
+ hMp4SpaceEnc->pppHybridIn__FDK[ch][nFrameTimeSlots + slot],
+ hMp4SpaceEnc->nHybridBands);
+ }
+ }
+ }
+
+ /*
+ * Onset Detection:
+ * - detection of transients
+ * - build framing
+ */
+ for (ch = 0; ch < nChIn; ch++) {
+ if (ch != 3) { /* !LFE */
+ if (SACENC_OK !=
+ (error = fdk_sacenc_onsetDetect_Apply(
+ hMp4SpaceEnc->phOnset[ch], nFrameTimeSlots,
+ hMp4SpaceEnc->nHybridBands,
+ &hMp4SpaceEnc->pppHybridIn__FDK
+ [ch][hMp4SpaceEnc->nAnalysisLookaheadTimeSlots],
+ encoderInputChScale[ch],
+ hMp4SpaceEnc->trPrevPos[1], /* contains previous Transient */
+ hMp4SpaceEnc->ppTrCurrPos[ch]))) {
+ goto bail;
+ }
+
+ if ((1) && (hMp4SpaceEnc->useFrameKeep == 0)) {
+ hMp4SpaceEnc->ppTrCurrPos[ch][0] = -1;
+ }
+
+ /* Find first Transient Position */
+ if ((hMp4SpaceEnc->ppTrCurrPos[ch][0] >= 0) &&
+ ((currTransPos < 0) ||
+ (hMp4SpaceEnc->ppTrCurrPos[ch][0] < currTransPos))) {
+ currTransPos = hMp4SpaceEnc->ppTrCurrPos[ch][0];
+ }
+ } /* !LFE */
+ } /* ch */
+
+ if (hMp4SpaceEnc->useFrameKeep == 1) {
+ if ((currTransPos != -1) || (hMp4SpaceEnc->independencyFlag == 1)) {
+ hMp4SpaceEnc->avoid_keep = NUM_KEEP_WINDOWS;
+ currTransPos = -1;
+ }
+ }
+
+ /* Save previous Transient Position */
+ hMp4SpaceEnc->trPrevPos[0] =
+ FDKmax(-1, hMp4SpaceEnc->trPrevPos[1] - (INT)nFrameTimeSlots);
+ hMp4SpaceEnc->trPrevPos[1] = currTransPos;
+
+ /* Update Onset Detection Energy Buffer */
+ for (ch = 0; ch < nChIn; ch++) {
+ if (SACENC_OK != (error = fdk_sacenc_onsetDetect_Update(
+ hMp4SpaceEnc->phOnset[ch], nFrameTimeSlots))) {
+ goto bail;
+ }
+ }
+
+ /* Framing */
+ if (SACENC_OK !=
+ (error = fdk_sacenc_frameWindow_GetWindow(
+ hMp4SpaceEnc->hFrameWindow, hMp4SpaceEnc->trPrevPos,
+ nFrameTimeSlots, &pFrameData->framingInfo,
+ hMp4SpaceEnc->pFrameWindowAna__FDK, &hMp4SpaceEnc->frameWinList,
+ hMp4SpaceEnc->avoid_keep))) {
+ goto bail;
+ }
+
+ /*
+ * MPS Processing:
+ */
+ for (ps = 0, winCnt = 0; ps < hMp4SpaceEnc->frameWinList.n; ++ps) {
+ /* Analysis Windowing */
+ if (hMp4SpaceEnc->frameWinList.dat[ps].hold == FW_HOLD) {
+ /* ************************************** */
+ /* ONLY COPY AND HOLD PREVIOUS PARAMETERS */
+ if (SACENC_OK != (error = fdk_sacenc_duplicateParameterSet(
+ &hMp4SpaceEnc->saveFrame, 0, pFrameData, ps))) {
+ goto bail;
+ }
+
+ } else { /* !FW_HOLD */
+ /* ************************************** */
+ /* NEW WINDOW */
+
+ INT nAnalysisLengthTimeSlots, analysisStartTimeSlot;
+
+ nAnalysisLengthTimeSlots = getAnalysisLengthTimeSlots(
+ hMp4SpaceEnc->pFrameWindowAna__FDK[winCnt],
+ hMp4SpaceEnc->nAnalysisLengthTimeSlots);
+
+ analysisStartTimeSlot =
+ getAnalysisStartTimeSlot(hMp4SpaceEnc->pFrameWindowAna__FDK[winCnt],
+ hMp4SpaceEnc->nAnalysisLengthTimeSlots);
+
+ /* perform main signal analysis windowing in
+ * fdk_sacenc_spaceTree_Apply() */
+ FIXP_WIN *pFrameWindowAna__FDK =
+ hMp4SpaceEnc->pFrameWindowAna__FDK[winCnt];
+ FIXP_DPK ***pppHybridIn__FDK = hMp4SpaceEnc->pppHybridIn__FDK;
+ FIXP_DPK ***pppProcDataIn__FDK = hMp4SpaceEnc->pppProcDataIn__FDK;
+
+ if (hMp4SpaceEnc->encMode == SACENC_212 &&
+ hMp4SpaceEnc->bEncMode_212_only) {
+ pppProcDataIn__FDK = pppHybridIn__FDK;
+ }
+
+ if (SACENC_OK !=
+ (error = fdk_sacenc_spaceTree_Apply(
+ hMp4SpaceEnc->hSpaceTree, ps, nChIn, nAnalysisLengthTimeSlots,
+ analysisStartTimeSlot, hMp4SpaceEnc->nHybridBands,
+ pFrameWindowAna__FDK, pppHybridIn__FDK,
+ pppProcDataIn__FDK, /* multi-channel input */
+ pFrameData, hMp4SpaceEnc->avoid_keep, encoderInputChScale))) {
+ goto bail;
+ }
+
+ /* Save spatial frame for potential hold parameter set */
+ if (SACENC_OK != (error = fdk_sacenc_duplicateParameterSet(
+ pFrameData, ps, &hMp4SpaceEnc->saveFrame, 0))) {
+ goto bail;
+ }
+
+ ++winCnt;
+ }
+ if (hMp4SpaceEnc->avoid_keep > 0) {
+ hMp4SpaceEnc->avoid_keep--;
+ }
+ } /* Loop over Parameter Sets */
+ /* ---- End of Processing Loop ---- */
+
+ /*
+ * Update hybridInReal/Imag buffer and do the same for arbDmx
+ * this means to move the hybrid data of the current frame to the beginning
+ * of the 2*nFrameLength-long buffer
+ */
+ if (!(hMp4SpaceEnc->encMode == SACENC_212 &&
+ hMp4SpaceEnc->bEncMode_212_only)) {
+ for (ch = 0; ch < nChIn; ch++) { /* for automatic downmix */
+ for (slot = 0;
+ slot < (int)(hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
+ nFrameTimeSlots - nFrameTimeSlotsReduction);
+ slot++) {
+ copyCplxVec(
+ hMp4SpaceEnc->pppHybridIn__FDK[ch][slot],
+ hMp4SpaceEnc->pppHybridIn__FDK[ch][nFrameTimeSlots + slot],
+ hMp4SpaceEnc->nHybridBands);
+ }
+ for (slot = 0; slot < nFrameTimeSlots; slot++) {
+ setCplxVec(
+ hMp4SpaceEnc->pppHybridIn__FDK
+ [ch][hMp4SpaceEnc->nUpdateHybridPositionTimeSlots +
+ nFrameTimeSlots - nFrameTimeSlotsReduction + slot],
+ (FIXP_DBL)0, hMp4SpaceEnc->nHybridBands);
+ }
+ }
+ }
+ /*
+ * Spatial Tonality:
+ */
+ {
+ /* Smooth config off. */
+ FDKmemclear(&pFrameData->smgData, sizeof(pFrameData->smgData));
+ }
+
+ /*
+ * Create bitstream
+ * - control independecy flag
+ * - write spatial frame
+ * - return bitstream
+ */
+ UCHAR *pBitstreamDelayBuffer;
+
+ if (hMp4SpaceEnc->encMode == SACENC_212) {
+ /* no bitstream delay buffer for SACENC_212 mode, write bitstream directly
+ * into the sacOutBuffer buffer which is provided by the core routine */
+ pBitstreamDelayBuffer = (UCHAR *)outBufDesc->ppBase[1];
+ } else {
+ /* bitstream delay is handled in ppBitstreamDelayBuffer buffer */
+ pBitstreamDelayBuffer =
+ hMp4SpaceEnc
+ ->ppBitstreamDelayBuffer[hMp4SpaceEnc->nBitstreamBufferWrite];
+ }
+ if (pBitstreamDelayBuffer == NULL) {
+ error = SACENC_INVALID_HANDLE;
+ goto bail;
+ }
+
+ pFrameData->bsIndependencyFlag = hMp4SpaceEnc->independencyFlag;
+
+ if (SACENC_OK !=
+ (error = fdk_sacenc_writeSpatialFrame(
+ pBitstreamDelayBuffer, MAX_MPEGS_BYTES,
+ &hMp4SpaceEnc->pnOutputBits[hMp4SpaceEnc->nBitstreamBufferWrite],
+ hMp4SpaceEnc->hBitstreamFormatter))) {
+ goto bail;
+ }
+
+ /* return bitstream info */
+ if ((hMp4SpaceEnc->nDiscardOutFrames == 0) &&
+ (getBufDescIdx(outBufDesc,
+ (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_BS_DATA)) != -1)) {
+ const INT idx = getBufDescIdx(
+ outBufDesc, (FDK_BUF_TYPE_OUTPUT | FDK_BUF_TYPE_BS_DATA));
+ const INT outBits =
+ hMp4SpaceEnc->pnOutputBits[hMp4SpaceEnc->nBitstreamBufferRead];
+
+ if (((outBits + 7) / 8) >
+ (INT)(outBufDesc->pBufSize[idx] / outBufDesc->pEleSize[idx])) {
+ outargs->nOutputBits = 0;
+ error = SACENC_ENCODE_ERROR;
+ goto bail;
+ }
+
+ /* return bitstream buffer, copy delayed bitstream for all configurations
+ * except for the SACENC_212 mode */
+ if (hMp4SpaceEnc->encMode != SACENC_212) {
+ FDKmemcpy(
+ outBufDesc->ppBase[idx],
+ hMp4SpaceEnc
+ ->ppBitstreamDelayBuffer[hMp4SpaceEnc->nBitstreamBufferRead],
+ (outBits + 7) / 8);
+ }
+
+ /* return number of valid bits */
+ outargs->nOutputBits = outBits;
+ } else { /* No spatial data should be returned if the current frame is to be
+ discarded. */
+ outargs->nOutputBits = 0;
+ }
+
+ /* update pointers */
+ hMp4SpaceEnc->nBitstreamBufferRead =
+ (hMp4SpaceEnc->nBitstreamBufferRead + 1) %
+ hMp4SpaceEnc->nBitstreamDelayBuffer;
+ hMp4SpaceEnc->nBitstreamBufferWrite =
+ (hMp4SpaceEnc->nBitstreamBufferWrite + 1) %
+ hMp4SpaceEnc->nBitstreamDelayBuffer;
+
+ /* Set Output Parameters */
+ nOutputSamples =
+ (hMp4SpaceEnc->nDiscardOutFrames == 0)
+ ? (nOutputSamplesMax)
+ : 0; /* don't output samples in case frames to be discarded */
+ if (nOutputSamples > nOutputSamplesBufferSize) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+ outargs->nOutputSamples = nOutputSamples;
+
+ { /* !bQmfOutput */
+
+ if (hMp4SpaceEnc->encMode != SACENC_212) {
+ /* delay output samples and interleave them */
+ /* note: in case of arbitrary downmix this will always be processed,
+ * because nOutputSamples != 0, even if bDMXAlign is switched on */
+ /* always run copy-func, so nOutputSamplesMax instead of nOutputSamples
+ */
+ for (ch = 0; ch < nChOut; ch++) {
+ FDKmemcpy_flex(
+ &hMp4SpaceEnc->pOutputDelayBuffer__FDK
+ [ch + (hMp4SpaceEnc->nOutputBufferDelay) * nChOut],
+ nChOut, hMp4SpaceEnc->ppTimeSigOut__FDK[ch], 1,
+ nOutputSamplesMax / nChOut);
+ }
+
+ /* write delayed data in output pcm stream */
+ /* always calculate, limiter must have a lookahead!!! */
+ FDKmemcpy(pOutputSamples, hMp4SpaceEnc->pOutputDelayBuffer__FDK,
+ nOutputSamplesMax * sizeof(INT_PCM));
+
+ /* update delay buffer (move back end to the beginning of the buffer) */
+ FDKmemmove(
+ hMp4SpaceEnc->pOutputDelayBuffer__FDK,
+ &hMp4SpaceEnc->pOutputDelayBuffer__FDK[nOutputSamplesMax],
+ nChOut * (hMp4SpaceEnc->nOutputBufferDelay) * sizeof(INT_PCM));
+ }
+
+ if (hMp4SpaceEnc->useTimeDomDownmix <= 0) {
+ if (SACENC_OK != (error = fdk_sacenc_staticPostGain_ApplyFDK(
+ hMp4SpaceEnc->hStaticGain, pOutputSamples,
+ nOutputSamplesMax, 0))) {
+ goto bail;
+ }
+ }
+
+ } /* !bQmfOutput */
+
+ if (hMp4SpaceEnc->nDiscardOutFrames > 0) {
+ hMp4SpaceEnc->nDiscardOutFrames--;
+ }
+
+ /* Invalidate Input Buffer */
+ hMp4SpaceEnc->nSamplesValid = 0;
+
+ } /* valid handle */
+bail:
+ return error;
+}
+
+FDK_SACENC_ERROR FDK_sacenc_close(HANDLE_MP4SPACE_ENCODER *phMp4SpaceEnc) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ if (NULL != phMp4SpaceEnc) {
+ if (NULL != *phMp4SpaceEnc) {
+ int ch, i;
+ HANDLE_MP4SPACE_ENCODER const hEnc = *phMp4SpaceEnc;
+
+ if (hEnc->pParameterBand2HybridBandOffset != NULL) {
+ FDK_FREE_MEMORY_1D(hEnc->pParameterBand2HybridBandOffset);
+ }
+ /* Free Analysis Filterbank Structs */
+ if (hEnc->pEncoderInputChScale != NULL) {
+ FDK_FREE_MEMORY_1D(hEnc->pEncoderInputChScale);
+ }
+ if (hEnc->staticTimeDomainDmxInScale != NULL) {
+ FDK_FREE_MEMORY_1D(hEnc->staticTimeDomainDmxInScale);
+ }
+ if (hEnc->phQmfFiltIn__FDK != NULL) {
+ for (ch = 0; ch < hEnc->setup.maxChIn; ch++) {
+ if (hEnc->phQmfFiltIn__FDK[ch] != NULL) {
+ if (hEnc->phQmfFiltIn__FDK[ch]->FilterStates != NULL) {
+ FDK_FREE_MEMORY_1D(hEnc->phQmfFiltIn__FDK[ch]->FilterStates);
+ }
+ FDK_FREE_MEMORY_1D(hEnc->phQmfFiltIn__FDK[ch]);
+ }
+ }
+ FDK_FREE_MEMORY_1D(hEnc->phQmfFiltIn__FDK);
+ }
+ for (ch = 0; ch < hEnc->setup.maxChIn; ch++) {
+ if (NULL != hEnc->phDCFilterSigIn[ch]) {
+ fdk_sacenc_destroyDCFilter(&hEnc->phDCFilterSigIn[ch]);
+ }
+ }
+ /* Close Onset Detection */
+ for (ch = 0; ch < hEnc->setup.maxChIn; ch++) {
+ if (NULL != hEnc->phOnset[ch]) {
+ fdk_sacenc_onsetDetect_Close(&hEnc->phOnset[ch]);
+ }
+ }
+ if (hEnc->ppTrCurrPos) {
+ FDK_FREE_MEMORY_2D(hEnc->ppTrCurrPos);
+ }
+ if (hEnc->hFrameWindow) {
+ fdk_sacenc_frameWindow_Destroy(&hEnc->hFrameWindow);
+ }
+ /* Close Space Tree */
+ if (NULL != hEnc->hSpaceTree) {
+ fdk_sacenc_spaceTree_Close(&hEnc->hSpaceTree);
+ }
+ if (NULL != hEnc->hEnhancedTimeDmx) {
+ fdk_sacenc_close_enhancedTimeDomainDmx(&hEnc->hEnhancedTimeDmx);
+ }
+ /* Close Static Gain */
+ if (NULL != hEnc->hStaticGain) {
+ fdk_sacenc_staticGain_Close(&hEnc->hStaticGain);
+ }
+ if (NULL != hEnc->hStaticGainConfig) {
+ fdk_sacenc_staticGain_CloseConfig(&hEnc->hStaticGainConfig);
+ }
+ /* Close Delay*/
+ if (NULL != hEnc->hDelay) {
+ fdk_sacenc_delay_Close(&hEnc->hDelay);
+ }
+ /* Delete Bitstream Stuff */
+ if (NULL != hEnc->hBitstreamFormatter) {
+ fdk_sacenc_destroySpatialBitstreamEncoder(&(hEnc->hBitstreamFormatter));
+ }
+ if (hEnc->pppHybridIn__FDK != NULL) {
+ if (hEnc->setup.bEncMode_212 == 1) {
+ FDK_FREE_MEMORY_3D(hEnc->pppHybridIn__FDK);
+ FDK_FREE_MEMORY_3D(hEnc->pppHybridInStatic__FDK);
+ } else {
+ FDK_FREE_MEMORY_3D(hEnc->pppHybridIn__FDK);
+ }
+ }
+ if (hEnc->pppProcDataIn__FDK != NULL) {
+ FDK_FREE_MEMORY_3D(hEnc->pppProcDataIn__FDK);
+ }
+ if (hEnc->pOutputDelayBuffer__FDK != NULL) {
+ FDK_FREE_MEMORY_1D(hEnc->pOutputDelayBuffer__FDK);
+ }
+ if (hEnc->ppTimeSigIn__FDK != NULL) {
+ { FDK_FREE_MEMORY_2D(hEnc->ppTimeSigIn__FDK); }
+ }
+ if (hEnc->ppTimeSigDelayIn__FDK != NULL) {
+ FDK_FREE_MEMORY_2D(hEnc->ppTimeSigDelayIn__FDK);
+ }
+ if (hEnc->ppTimeSigOut__FDK != NULL) {
+ FDK_FREE_MEMORY_2D(hEnc->ppTimeSigOut__FDK);
+ }
+ for (i = 0; i < MAX_NUM_PARAMS; i++) {
+ if (hEnc->pFrameWindowAna__FDK[i] != NULL) {
+ FDK_FREE_MEMORY_1D(hEnc->pFrameWindowAna__FDK[i]);
+ }
+ }
+ if (hEnc->pnOutputBits != NULL) {
+ FDK_FREE_MEMORY_1D(hEnc->pnOutputBits);
+ }
+ if (hEnc->ppBitstreamDelayBuffer != NULL) {
+ FDK_FREE_MEMORY_2D(hEnc->ppBitstreamDelayBuffer);
+ }
+ if (hEnc->sscBuf.pSsc != NULL) {
+ FDK_FREE_MEMORY_1D(hEnc->sscBuf.pSsc);
+ }
+ FDK_FREE_MEMORY_1D(*phMp4SpaceEnc);
+ }
+ }
+
+ return error;
+}
+
+/*-----------------------------------------------------------------------------
+ functionname: mp4SpaceEnc_InitDelayCompensation()
+ description: initialzes delay compensation
+ returns: noError on success, an apropriate error code else
+ -----------------------------------------------------------------------------*/
+static FDK_SACENC_ERROR mp4SpaceEnc_InitDelayCompensation(
+ HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc, const INT coreCoderDelay) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ /* Sanity Check */
+ if (hMp4SpaceEnc == NULL) {
+ error = SACENC_INVALID_HANDLE;
+ } else {
+ hMp4SpaceEnc->coreCoderDelay = coreCoderDelay;
+
+ if (SACENC_OK != (error = fdk_sacenc_delay_Init(
+ hMp4SpaceEnc->hDelay, hMp4SpaceEnc->nQmfBands,
+ hMp4SpaceEnc->nFrameLength, coreCoderDelay,
+ hMp4SpaceEnc->timeAlignment))) {
+ goto bail;
+ }
+
+ fdk_sacenc_delay_SetDmxAlign(hMp4SpaceEnc->hDelay, 0);
+ fdk_sacenc_delay_SetTimeDomDmx(
+ hMp4SpaceEnc->hDelay, (hMp4SpaceEnc->useTimeDomDownmix >= 1) ? 1 : 0);
+ fdk_sacenc_delay_SetMinimizeDelay(hMp4SpaceEnc->hDelay, 1);
+
+ if (SACENC_OK != (error = fdk_sacenc_delay_SubCalulateBufferDelays(
+ hMp4SpaceEnc->hDelay))) {
+ goto bail;
+ }
+
+ /* init output delay compensation */
+ hMp4SpaceEnc->nBitstreamDelayBuffer =
+ fdk_sacenc_delay_GetBitstreamFrameBufferSize(hMp4SpaceEnc->hDelay);
+ hMp4SpaceEnc->nOutputBufferDelay =
+ fdk_sacenc_delay_GetOutputAudioBufferDelay(hMp4SpaceEnc->hDelay);
+ hMp4SpaceEnc->nSurroundAnalysisBufferDelay =
+ fdk_sacenc_delay_GetSurroundAnalysisBufferDelay(hMp4SpaceEnc->hDelay);
+ hMp4SpaceEnc->nBitstreamBufferRead = 0;
+ hMp4SpaceEnc->nBitstreamBufferWrite =
+ hMp4SpaceEnc->nBitstreamDelayBuffer - 1;
+
+ if (hMp4SpaceEnc->encMode == SACENC_212) {
+ /* mode 212 expects no bitstream delay */
+ if (hMp4SpaceEnc->nBitstreamBufferWrite !=
+ hMp4SpaceEnc->nBitstreamBufferRead) {
+ error = SACENC_PARAM_ERROR;
+ goto bail;
+ }
+
+ /* mode 212 expects no output buffer delay */
+ if (hMp4SpaceEnc->nOutputBufferDelay != 0) {
+ error = SACENC_PARAM_ERROR;
+ goto bail;
+ }
+ }
+
+ /*** Input delay to obtain a net encoder delay that is a multiple
+ of the used framelength to ensure synchronization of framing
+ in artistic down-mix with the corresponding spatial data. ***/
+ hMp4SpaceEnc->nDiscardOutFrames =
+ fdk_sacenc_delay_GetDiscardOutFrames(hMp4SpaceEnc->hDelay);
+ hMp4SpaceEnc->nInputDelay =
+ fdk_sacenc_delay_GetDmxAlignBufferDelay(hMp4SpaceEnc->hDelay);
+
+ /* reset independency Flag counter */
+ hMp4SpaceEnc->independencyCount = 0;
+ hMp4SpaceEnc->independencyFlag = 1;
+
+ int i;
+
+ /* write some parameters to bitstream */
+ for (i = 0; i < hMp4SpaceEnc->nBitstreamDelayBuffer - 1; i++) {
+ SPATIALFRAME *pFrameData = NULL;
+
+ if (NULL == (pFrameData = fdk_sacenc_getSpatialFrame(
+ hMp4SpaceEnc->hBitstreamFormatter, READ_SPATIALFRAME))) {
+ error = SACENC_INVALID_HANDLE;
+ goto bail;
+ }
+
+ pFrameData->bsIndependencyFlag = 1;
+ pFrameData->framingInfo.numParamSets = 1;
+ pFrameData->framingInfo.bsFramingType = 0;
+
+ fdk_sacenc_writeSpatialFrame(
+ hMp4SpaceEnc->ppBitstreamDelayBuffer[i], MAX_MPEGS_BYTES,
+ &hMp4SpaceEnc->pnOutputBits[i], hMp4SpaceEnc->hBitstreamFormatter);
+ }
+
+ if ((hMp4SpaceEnc->nInputDelay > MAX_DELAY_INPUT) ||
+ (hMp4SpaceEnc->nOutputBufferDelay > MAX_DELAY_OUTPUT) ||
+ (hMp4SpaceEnc->nSurroundAnalysisBufferDelay >
+ MAX_DELAY_SURROUND_ANALYSIS) ||
+ (hMp4SpaceEnc->nBitstreamDelayBuffer > MAX_BITSTREAM_DELAY)) {
+ error = SACENC_INIT_ERROR;
+ goto bail;
+ }
+ }
+
+bail:
+
+ return error;
+}
+
+static QUANTMODE __mapQuantMode(const MP4SPACEENC_QUANTMODE quantMode) {
+ QUANTMODE bsQuantMode = QUANTMODE_INVALID;
+
+ switch (quantMode) {
+ case SACENC_QUANTMODE_FINE:
+ bsQuantMode = QUANTMODE_FINE;
+ break;
+ case SACENC_QUANTMODE_EBQ1:
+ bsQuantMode = QUANTMODE_EBQ1;
+ break;
+ case SACENC_QUANTMODE_EBQ2:
+ bsQuantMode = QUANTMODE_EBQ2;
+ break;
+ case SACENC_QUANTMODE_RSVD3:
+ case SACENC_QUANTMODE_INVALID:
+ default:
+ bsQuantMode = QUANTMODE_INVALID;
+ } /* switch hEnc->quantMode */
+
+ return bsQuantMode;
+}
+
+static FDK_SACENC_ERROR FillSpatialSpecificConfig(
+ const HANDLE_MP4SPACE_ENCODER hEnc, SPATIALSPECIFICCONFIG *const hSsc) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ if ((NULL == hEnc) || (NULL == hSsc)) {
+ error = SACENC_INVALID_HANDLE;
+ } else {
+ SPACE_TREE_DESCRIPTION spaceTreeDescription;
+ int i;
+
+ /* Get tree description */
+ if (SACENC_OK != (error = fdk_sacenc_spaceTree_GetDescription(
+ hEnc->hSpaceTree, &spaceTreeDescription))) {
+ goto bail;
+ }
+
+ /* Fill SSC */
+ FDKmemclear(hSsc, sizeof(SPATIALSPECIFICCONFIG)); /* reset */
+
+ hSsc->numBands = hEnc->spaceTreeSetup.nParamBands; /* for bsFreqRes */
+
+ /* Fill tree configuration */
+ hSsc->treeDescription.numOttBoxes = spaceTreeDescription.nOttBoxes;
+ hSsc->treeDescription.numInChan = spaceTreeDescription.nInChannels;
+ hSsc->treeDescription.numOutChan = spaceTreeDescription.nOutChannels;
+
+ for (i = 0; i < SACENC_MAX_NUM_BOXES; i++) {
+ hSsc->ottConfig[i].bsOttBands = hSsc->numBands;
+ }
+
+ switch (hEnc->encMode) {
+ case SACENC_212:
+ hSsc->bsTreeConfig = TREE_212;
+ break;
+ case SACENC_INVALID_MODE:
+ default:
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+
+ hSsc->bsSamplingFrequency =
+ hEnc->nSampleRate; /* for bsSamplingFrequencyIndex */
+ hSsc->bsFrameLength = hEnc->nFrameTimeSlots - 1;
+
+ /* map decorr type */
+ if (DECORR_INVALID ==
+ (hSsc->bsDecorrConfig = mp4SpaceEnc_GetDecorrConfig(hEnc->encMode))) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+
+ /* map quantMode */
+ if (QUANTMODE_INVALID ==
+ (hSsc->bsQuantMode = __mapQuantMode(hEnc->quantMode))) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+
+ /* Configure Gains*/
+ hSsc->bsFixedGainDMX = fdk_sacenc_staticGain_GetDmxGain(hEnc->hStaticGain);
+ hSsc->bsEnvQuantMode = 0;
+
+ } /* valid handle */
+
+bail:
+ return error;
+}
+
+static FDK_SACENC_ERROR mp4SpaceEnc_FillSpaceTreeSetup(
+ const HANDLE_MP4SPACE_ENCODER hEnc,
+ SPACE_TREE_SETUP *const hSpaceTreeSetup) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ /* Sanity Check */
+ if (NULL == hEnc || NULL == hSpaceTreeSetup) {
+ error = SACENC_INVALID_HANDLE;
+ } else {
+ QUANTMODE tmpQuantmode = QUANTMODE_INVALID;
+
+ /* map quantMode */
+ if (QUANTMODE_INVALID == (tmpQuantmode = __mapQuantMode(hEnc->quantMode))) {
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ }
+
+ hSpaceTreeSetup->nParamBands = hEnc->nParamBands;
+ hSpaceTreeSetup->bUseCoarseQuantTtoCld = hEnc->useCoarseQuantCld;
+ hSpaceTreeSetup->bUseCoarseQuantTtoIcc = hEnc->useCoarseQuantIcc;
+ hSpaceTreeSetup->quantMode = tmpQuantmode;
+ hSpaceTreeSetup->nHybridBandsMax = hEnc->nHybridBands;
+
+ switch (hEnc->encMode) {
+ case SACENC_212:
+ hSpaceTreeSetup->mode = SPACETREE_212;
+ hSpaceTreeSetup->nChannelsInMax = 2;
+ break;
+ case SACENC_INVALID_MODE:
+ default:
+ error = SACENC_INVALID_CONFIG;
+ goto bail;
+ } /* switch hEnc->encMode */
+
+ } /* valid handle */
+bail:
+ return error;
+}
+
+FDK_SACENC_ERROR FDK_sacenc_getInfo(const HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,
+ MP4SPACEENC_INFO *const pInfo) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ if ((NULL == hMp4SpaceEnc) || (NULL == pInfo)) {
+ error = SACENC_INVALID_HANDLE;
+ } else {
+ pInfo->nSampleRate = hMp4SpaceEnc->nSampleRate;
+ pInfo->nSamplesFrame = hMp4SpaceEnc->nFrameLength;
+ pInfo->nTotalInputChannels = hMp4SpaceEnc->nInputChannels;
+ pInfo->nDmxDelay = fdk_sacenc_delay_GetInfoDmxDelay(hMp4SpaceEnc->hDelay);
+ pInfo->nCodecDelay =
+ fdk_sacenc_delay_GetInfoCodecDelay(hMp4SpaceEnc->hDelay);
+ pInfo->nDecoderDelay =
+ fdk_sacenc_delay_GetInfoDecoderDelay(hMp4SpaceEnc->hDelay);
+ pInfo->nPayloadDelay =
+ fdk_sacenc_delay_GetBitstreamFrameBufferSize(hMp4SpaceEnc->hDelay) - 1;
+ pInfo->nDiscardOutFrames = hMp4SpaceEnc->nDiscardOutFrames;
+
+ pInfo->pSscBuf = &hMp4SpaceEnc->sscBuf;
+ }
+ return error;
+}
+
+FDK_SACENC_ERROR FDK_sacenc_setParam(HANDLE_MP4SPACE_ENCODER hMp4SpaceEnc,
+ const SPACEENC_PARAM param,
+ const UINT value) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ /* check encoder handle */
+ if (hMp4SpaceEnc == NULL) {
+ error = SACENC_INVALID_HANDLE;
+ goto bail;
+ }
+
+ /* apply param value */
+ switch (param) {
+ case SACENC_LOWDELAY:
+ if (!((value == 0) || (value == 1) || (value == 2))) {
+ error = SACENC_INVALID_CONFIG;
+ break;
+ }
+ hMp4SpaceEnc->user.bLdMode = value;
+ break;
+
+ case SACENC_ENC_MODE:
+ switch ((MP4SPACEENC_MODE)value) {
+ case SACENC_212:
+ hMp4SpaceEnc->user.encMode = (MP4SPACEENC_MODE)value;
+ break;
+ default:
+ error = SACENC_INVALID_CONFIG;
+ }
+ break;
+
+ case SACENC_SAMPLERATE:
+ if (((int)value < 0) ||
+ ((int)value > hMp4SpaceEnc->setup.maxSamplingrate)) {
+ error = SACENC_INVALID_CONFIG;
+ break;
+ }
+ hMp4SpaceEnc->user.sampleRate = value;
+ break;
+
+ case SACENC_FRAME_TIME_SLOTS:
+ if (((int)value < 0) ||
+ ((int)value > hMp4SpaceEnc->setup.maxFrameTimeSlots)) {
+ error = SACENC_INVALID_CONFIG;
+ break;
+ }
+ hMp4SpaceEnc->user.frameTimeSlots = value;
+ break;
+
+ case SACENC_PARAM_BANDS:
+ switch ((MP4SPACEENC_BANDS_CONFIG)value) {
+ case SACENC_BANDS_4:
+ case SACENC_BANDS_5:
+ case SACENC_BANDS_7:
+ case SACENC_BANDS_9:
+ case SACENC_BANDS_12:
+ case SACENC_BANDS_15:
+ case SACENC_BANDS_23:
+ hMp4SpaceEnc->user.nParamBands = (MP4SPACEENC_BANDS_CONFIG)value;
+ break;
+ default:
+ error = SACENC_INVALID_CONFIG;
+ }
+ break;
+
+ case SACENC_TIME_DOM_DMX:
+ if (!((value == 0) || (value == 2))) {
+ error = SACENC_INVALID_CONFIG;
+ break;
+ }
+ hMp4SpaceEnc->user.bTimeDomainDmx = value;
+ break;
+
+ case SACENC_DMX_GAIN:
+ if (!((value == 0) || (value == 1) || (value == 2) || (value == 3) ||
+ (value == 4) || (value == 5) || (value == 6) || (value == 7))) {
+ error = SACENC_INVALID_CONFIG;
+ break;
+ }
+ error = fdk_sacenc_staticGain_SetDmxGain(hMp4SpaceEnc->hStaticGainConfig,
+ (MP4SPACEENC_DMX_GAIN)value);
+ break;
+
+ case SACENC_COARSE_QUANT:
+ if (!((value == 0) || (value == 1))) {
+ error = SACENC_INVALID_CONFIG;
+ break;
+ }
+ hMp4SpaceEnc->user.bUseCoarseQuant = value;
+ break;
+
+ case SACENC_QUANT_MODE:
+ switch ((MP4SPACEENC_QUANTMODE)value) {
+ case SACENC_QUANTMODE_FINE:
+ case SACENC_QUANTMODE_EBQ1:
+ case SACENC_QUANTMODE_EBQ2:
+ hMp4SpaceEnc->user.quantMode = (MP4SPACEENC_QUANTMODE)value;
+ break;
+ default:
+ error = SACENC_INVALID_CONFIG;
+ }
+ break;
+
+ case SACENC_TIME_ALIGNMENT:
+ if ((INT)value < -32768 || (INT)value > 32767) {
+ error = SACENC_INVALID_CONFIG;
+ break;
+ }
+ hMp4SpaceEnc->user.timeAlignment = value;
+ break;
+
+ case SACENC_INDEPENDENCY_COUNT:
+ hMp4SpaceEnc->independencyCount = value;
+ break;
+
+ case SACENC_INDEPENDENCY_FACTOR:
+ hMp4SpaceEnc->user.independencyFactor = value;
+ break;
+
+ default:
+ error = SACENC_UNSUPPORTED_PARAMETER;
+ break;
+ } /* switch(param) */
+bail:
+ return error;
+}
+
+FDK_SACENC_ERROR FDK_sacenc_getLibInfo(LIB_INFO *info) {
+ int i = 0;
+
+ if (info == NULL) {
+ return SACENC_INVALID_HANDLE;
+ }
+
+ FDK_toolsGetLibInfo(info);
+
+ /* search for next free tab */
+ for (i = 0; i < FDK_MODULE_LAST; i++) {
+ if (info[i].module_id == FDK_NONE) break;
+ }
+ if (i == FDK_MODULE_LAST) {
+ return SACENC_INIT_ERROR;
+ }
+
+ info[i].module_id = FDK_MPSENC;
+ info[i].build_date = SACENC_LIB_BUILD_DATE;
+ info[i].build_time = SACENC_LIB_BUILD_TIME;
+ info[i].title = SACENC_LIB_TITLE;
+ info[i].version = LIB_VERSION(SACENC_LIB_VL0, SACENC_LIB_VL1, SACENC_LIB_VL2);
+ LIB_VERSION_STRING(&info[i]);
+
+ /* Capability flags */
+ info[i].flags = 0;
+ /* End of flags */
+
+ return SACENC_OK;
+}
+
+static DECORRCONFIG mp4SpaceEnc_GetDecorrConfig(
+ const MP4SPACEENC_MODE encMode) {
+ DECORRCONFIG decorrConfig = DECORR_INVALID;
+
+ /* set decorrConfig dependent on tree mode */
+ switch (encMode) {
+ case SACENC_212:
+ decorrConfig = DECORR_QMFSPLIT0;
+ break;
+ case SACENC_INVALID_MODE:
+ default:
+ decorrConfig = DECORR_INVALID;
+ }
+ return decorrConfig;
+}
+
+static FDK_SACENC_ERROR mp4SpaceEnc_InitNumParamBands(
+ HANDLE_MP4SPACE_ENCODER hEnc, const MP4SPACEENC_BANDS_CONFIG nParamBands) {
+ FDK_SACENC_ERROR error = SACENC_OK;
+
+ /* Set/Check nParamBands */
+ int k = 0;
+ const int n = sizeof(pValidBands_Ld) / sizeof(UCHAR);
+ const UCHAR *pBands = pValidBands_Ld;
+
+ while (k < n && pBands[k] != (UCHAR)nParamBands) ++k;
+ if (k == n) {
+ hEnc->nParamBands = SACENC_BANDS_INVALID;
+ } else {
+ hEnc->nParamBands = nParamBands;
+ }
+ return error;
+}