diff options
author | Matthias P. Braendli <matthias.braendli@mpb.li> | 2019-11-11 11:38:02 +0100 |
---|---|---|
committer | Matthias P. Braendli <matthias.braendli@mpb.li> | 2019-11-11 11:38:02 +0100 |
commit | 0e5af65c467b2423a0b857ae3ad98c91acc1e190 (patch) | |
tree | d07f69550d8886271e44fe79c4dcfb299cafbd38 /fdk-aac/libSACenc/src/sacenc_lib.cpp | |
parent | efe406d9724f959c8bc2a31802559ca6d41fd897 (diff) | |
download | ODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.tar.gz ODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.tar.bz2 ODR-AudioEnc-0e5af65c467b2423a0b857ae3ad98c91acc1e190.zip |
Include patched FDK-AAC in the repository
The initial idea was to get the DAB+ patch into upstream, but since
that follows the android source releases, there is no place for a custom
DAB+ patch there.
So instead of having to maintain a patched fdk-aac that has to have the
same .so version as the distribution package on which it is installed,
we prefer having a separate fdk-aac-dab library to avoid collision.
At that point, there's no reason to keep fdk-aac in a separate
repository, as odr-audioenc is the only tool that needs DAB+ encoding
support. Including it here simplifies installation, and makes it
consistent with toolame-dab, also shipped in this repository.
DAB+ decoding support (needed by ODR-SourceCompanion, dablin, etisnoop,
welle.io and others) can be done using upstream FDK-AAC.
Diffstat (limited to 'fdk-aac/libSACenc/src/sacenc_lib.cpp')
-rw-r--r-- | fdk-aac/libSACenc/src/sacenc_lib.cpp | 2042 |
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; +} |