1
2/* -----------------------------------------------------------------------------------------------------------
3Software License for The Fraunhofer FDK AAC Codec Library for Android
4
5© Copyright  1995 - 2012 Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V.
6  All rights reserved.
7
8 1.    INTRODUCTION
9The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software that implements
10the MPEG Advanced Audio Coding ("AAC") encoding and decoding scheme for digital audio.
11This FDK AAC Codec software is intended to be used on a wide variety of Android devices.
12
13AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient general perceptual
14audio codecs. AAC-ELD is considered the best-performing full-bandwidth communications codec by
15independent studies and is widely deployed. AAC has been standardized by ISO and IEC as part
16of the MPEG specifications.
17
18Patent licenses for necessary patent claims for the FDK AAC Codec (including those of Fraunhofer)
19may be obtained through Via Licensing (www.vialicensing.com) or through the respective patent owners
20individually for the purpose of encoding or decoding bit streams in products that are compliant with
21the ISO/IEC MPEG audio standards. Please note that most manufacturers of Android devices already license
22these patent claims through Via Licensing or directly from the patent owners, and therefore FDK AAC Codec
23software may already be covered under those patent licenses when it is used for those licensed purposes only.
24
25Commercially-licensed AAC software libraries, including floating-point versions with enhanced sound quality,
26are also available from Fraunhofer. Users are encouraged to check the Fraunhofer website for additional
27applications information and documentation.
28
292.    COPYRIGHT LICENSE
30
31Redistribution and use in source and binary forms, with or without modification, are permitted without
32payment of copyright license fees provided that you satisfy the following conditions:
33
34You must retain the complete text of this software license in redistributions of the FDK AAC Codec or
35your modifications thereto in source code form.
36
37You must retain the complete text of this software license in the documentation and/or other materials
38provided with redistributions of the FDK AAC Codec or your modifications thereto in binary form.
39You must make available free of charge copies of the complete source code of the FDK AAC Codec and your
40modifications thereto to recipients of copies in binary form.
41
42The name of Fraunhofer may not be used to endorse or promote products derived from this library without
43prior written permission.
44
45You may not charge copyright license fees for anyone to use, copy or distribute the FDK AAC Codec
46software or your modifications thereto.
47
48Your modified versions of the FDK AAC Codec must carry prominent notices stating that you changed the software
49and the date of any change. For modified versions of the FDK AAC Codec, the term
50"Fraunhofer FDK AAC Codec Library for Android" must be replaced by the term
51"Third-Party Modified Version of the Fraunhofer FDK AAC Codec Library for Android."
52
533.    NO PATENT LICENSE
54
55NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without limitation the patents of Fraunhofer,
56ARE GRANTED BY THIS SOFTWARE LICENSE. Fraunhofer provides no warranty of patent non-infringement with
57respect to this software.
58
59You may use this FDK AAC Codec software or modifications thereto only for purposes that are authorized
60by appropriate patent licenses.
61
624.    DISCLAIMER
63
64This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright holders and contributors
65"AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, including but not limited to the implied warranties
66of merchantability and fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
67CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary, or consequential damages,
68including but not limited to procurement of substitute goods or services; loss of use, data, or profits,
69or business interruption, however caused and on any theory of liability, whether in contract, strict
70liability, or tort (including negligence), arising in any way out of the use of this software, even if
71advised of the possibility of such damage.
72
735.    CONTACT INFORMATION
74
75Fraunhofer Institute for Integrated Circuits IIS
76Attention: Audio and Multimedia Departments - FDK AAC LL
77Am Wolfsmantel 33
7891058 Erlangen, Germany
79
80www.iis.fraunhofer.de/amm
81amm-info@iis.fraunhofer.de
82----------------------------------------------------------------------------------------------------------- */
83
84/*****************************  MPEG-4 AAC Encoder  **************************
85
86   Author(s):   M. Werner
87   Description: Block switching
88
89******************************************************************************/
90
91/****************** Includes *****************************/
92
93#include "block_switch.h"
94#include "genericStds.h"
95
96
97#define LOWOV_WINDOW _LOWOV_WINDOW
98
99/**************** internal function prototypes ***********/
100
101static FIXP_DBL FDKaacEnc_GetWindowEnergy(const FIXP_DBL in[], const INT blSwWndIdx);
102
103static void FDKaacEnc_CalcWindowEnergy( BLOCK_SWITCHING_CONTROL *RESTRICT blockSwitchingControl,
104                              INT                      windowLen);
105
106
107/****************** Constants *****************************/
108/*                                                LONG         START        SHORT         STOP         LOWOV                  */
109static const INT blockType2windowShape[2][5] = { {SINE_WINDOW, KBD_WINDOW,  WRONG_WINDOW, SINE_WINDOW, KBD_WINDOW},     /* LD */
110                                                 {KBD_WINDOW,  SINE_WINDOW, SINE_WINDOW,  KBD_WINDOW,  WRONG_WINDOW} }; /* LC */
111
112/* IIR high pass coeffs */
113
114#ifndef SINETABLE_16BIT
115
116static const FIXP_DBL hiPassCoeff[BLOCK_SWITCHING_IIR_LEN]=
117{
118  FL2FXCONST_DBL(-0.5095),FL2FXCONST_DBL(0.7548)
119};
120
121static const FIXP_DBL accWindowNrgFac = FL2FXCONST_DBL(0.3f);                   /* factor for accumulating filtered window energies */
122static const FIXP_DBL oneMinusAccWindowNrgFac = FL2FXCONST_DBL(0.7f);
123/* static const float attackRatio = 10.0; */                                    /* lower ratio limit for attacks */
124static const FIXP_DBL invAttackRatio = FL2FXCONST_DBL(0.1f);                    /* inverted lower ratio limit for attacks */
125
126/* The next constants are scaled, because they are used for comparison with scaled values*/
127/* minimum energy for attacks */
128static const FIXP_DBL minAttackNrg = (FL2FXCONST_DBL(1e+6f*NORM_PCM_ENERGY)>>BLOCK_SWITCH_ENERGY_SHIFT); /* minimum energy for attacks */
129
130#else
131
132static const FIXP_SGL hiPassCoeff[BLOCK_SWITCHING_IIR_LEN]=
133{
134  FL2FXCONST_SGL(-0.5095),FL2FXCONST_SGL(0.7548)
135};
136
137static const FIXP_DBL accWindowNrgFac = FL2FXCONST_DBL(0.3f);                   /* factor for accumulating filtered window energies */
138static const FIXP_SGL oneMinusAccWindowNrgFac = FL2FXCONST_SGL(0.7f);
139/* static const float attackRatio = 10.0; */                                    /* lower ratio limit for attacks */
140static const FIXP_SGL invAttackRatio = FL2FXCONST_SGL(0.1f);                    /* inverted lower ratio limit for attacks */
141/* minimum energy for attacks */
142static const FIXP_DBL minAttackNrg = (FL2FXCONST_DBL(1e+6f*NORM_PCM_ENERGY)>>BLOCK_SWITCH_ENERGY_SHIFT); /* minimum energy for attacks */
143
144#endif
145
146/**************** internal function prototypes ***********/
147
148static INT FDKaacEnc_GetWindowIndex(INT blockSwWindowIndex);
149
150static FIXP_DBL FDKaacEnc_GetWindowEnergy(const FIXP_DBL in[], const INT shortWndIdx);
151
152static void FDKaacEnc_CalcWindowEnergy( BLOCK_SWITCHING_CONTROL *RESTRICT blockSwitchingControl,
153                                        INT windowLen);
154
155
156
157/****************** Routines ****************************/
158void FDKaacEnc_InitBlockSwitching(BLOCK_SWITCHING_CONTROL *blockSwitchingControl, INT isLowDelay)
159{
160  /* note: the pointer to timeSignal can be zeroed here, because it is initialized for every call
161           to FDKaacEnc_BlockSwitching anew */
162  FDKmemclear (blockSwitchingControl, sizeof(BLOCK_SWITCHING_CONTROL));
163
164  if (isLowDelay)
165  {
166    blockSwitchingControl->nBlockSwitchWindows = 4;
167    blockSwitchingControl->allowShortFrames    = 0;
168    blockSwitchingControl->allowLookAhead      = 0;
169  }
170  else
171  {
172    blockSwitchingControl->nBlockSwitchWindows = 8;
173    blockSwitchingControl->allowShortFrames    = 1;
174    blockSwitchingControl->allowLookAhead      = 1;
175  }
176
177  blockSwitchingControl->noOfGroups            = MAX_NO_OF_GROUPS;
178
179  /* Initialize startvalue for blocktype */
180  blockSwitchingControl->lastWindowSequence    = LONG_WINDOW;
181  blockSwitchingControl->windowShape           = blockType2windowShape[blockSwitchingControl->allowShortFrames][blockSwitchingControl->lastWindowSequence];
182
183}
184
185static const INT suggestedGroupingTable[TRANS_FAC][MAX_NO_OF_GROUPS] =
186{
187    /* Attack in Window 0 */ {1,  3,  3,  1},
188    /* Attack in Window 1 */ {1,  1,  3,  3},
189    /* Attack in Window 2 */ {2,  1,  3,  2},
190    /* Attack in Window 3 */ {3,  1,  3,  1},
191    /* Attack in Window 4 */ {3,  1,  1,  3},
192    /* Attack in Window 5 */ {3,  2,  1,  2},
193    /* Attack in Window 6 */ {3,  3,  1,  1},
194    /* Attack in Window 7 */ {3,  3,  1,  1}
195};
196
197/* change block type depending on current blocktype and whether there's an attack */
198/* assume no look-ahead */
199static const INT chgWndSq[2][N_BLOCKTYPES] =
200{
201  /*             LONG WINDOW   START_WINDOW  SHORT_WINDOW  STOP_WINDOW,  LOWOV_WINDOW, WRONG_WINDOW */
202  /*no attack*/ {LONG_WINDOW,  STOP_WINDOW,  WRONG_WINDOW, LONG_WINDOW,  STOP_WINDOW , WRONG_WINDOW },
203  /*attack   */ {START_WINDOW, LOWOV_WINDOW, WRONG_WINDOW, START_WINDOW, LOWOV_WINDOW, WRONG_WINDOW }
204};
205
206/* change block type depending on current blocktype and whether there's an attack */
207/* assume look-ahead */
208static const INT chgWndSqLkAhd[2][2][N_BLOCKTYPES] =
209{
210  /*attack         LONG WINDOW    START_WINDOW   SHORT_WINDOW   STOP_WINDOW   LOWOV_WINDOW, WRONG_WINDOW */  /* last attack */
211  /*no attack*/ { {LONG_WINDOW,   SHORT_WINDOW,  STOP_WINDOW,   LONG_WINDOW,  WRONG_WINDOW, WRONG_WINDOW},   /* no attack   */
212  /*attack   */   {START_WINDOW,  SHORT_WINDOW,  SHORT_WINDOW,  START_WINDOW, WRONG_WINDOW, WRONG_WINDOW} }, /* no attack   */
213  /*no attack*/ { {LONG_WINDOW,   SHORT_WINDOW,  SHORT_WINDOW,  LONG_WINDOW,  WRONG_WINDOW, WRONG_WINDOW},   /* attack      */
214  /*attack   */   {START_WINDOW,  SHORT_WINDOW,  SHORT_WINDOW,  START_WINDOW, WRONG_WINDOW, WRONG_WINDOW} }  /* attack      */
215};
216
217int FDKaacEnc_BlockSwitching(BLOCK_SWITCHING_CONTROL *blockSwitchingControl, const INT granuleLength, const int isLFE)
218{
219    UINT i;
220    FIXP_DBL enM1, enMax;
221
222    UINT nBlockSwitchWindows = blockSwitchingControl->nBlockSwitchWindows;
223
224    /* for LFE : only LONG window allowed */
225    if (isLFE) {
226
227      /* case LFE: */
228      /* only long blocks, always use sine windows (MPEG2 AAC, MPEG4 AAC) */
229      blockSwitchingControl->lastWindowSequence = LONG_WINDOW;
230      blockSwitchingControl->windowShape    = SINE_WINDOW;
231      blockSwitchingControl->noOfGroups     = 1;
232      blockSwitchingControl->groupLen[0]    = 1;
233
234      return(0);
235    };
236
237    /* Save current attack index as last attack index */
238    blockSwitchingControl->lastattack = blockSwitchingControl->attack;
239    blockSwitchingControl->lastAttackIndex = blockSwitchingControl->attackIndex;
240
241    /* Save current window energy as last window energy */
242    FDKmemcpy(blockSwitchingControl->windowNrg[0], blockSwitchingControl->windowNrg[1], sizeof(blockSwitchingControl->windowNrg[0]));
243    FDKmemcpy(blockSwitchingControl->windowNrgF[0], blockSwitchingControl->windowNrgF[1], sizeof(blockSwitchingControl->windowNrgF[0]));
244
245    if (blockSwitchingControl->allowShortFrames)
246    {
247      /* Calculate suggested grouping info for the last frame */
248
249      /* Reset grouping info */
250      FDKmemclear (blockSwitchingControl->groupLen, sizeof(blockSwitchingControl->groupLen));
251
252      /* Set grouping info */
253      blockSwitchingControl->noOfGroups = MAX_NO_OF_GROUPS;
254
255      FDKmemcpy(blockSwitchingControl->groupLen, suggestedGroupingTable[blockSwitchingControl->lastAttackIndex], sizeof(blockSwitchingControl->groupLen));
256
257      if (blockSwitchingControl->attack == TRUE)
258          blockSwitchingControl->maxWindowNrg = FDKaacEnc_GetWindowEnergy(blockSwitchingControl->windowNrg[0], blockSwitchingControl->lastAttackIndex);
259      else
260          blockSwitchingControl->maxWindowNrg = FL2FXCONST_DBL(0.0);
261
262    }
263
264
265    /* Calculate unfiltered and filtered energies in subwindows and combine to segments */
266    FDKaacEnc_CalcWindowEnergy(blockSwitchingControl, granuleLength>>(nBlockSwitchWindows==4? 2:3 ));
267
268    /* now calculate if there is an attack */
269
270    /* reset attack */
271    blockSwitchingControl->attack = FALSE;
272
273    /* look for attack */
274    enMax = FL2FXCONST_DBL(0.0f);
275    enM1 = blockSwitchingControl->windowNrgF[0][nBlockSwitchWindows-1];
276
277    for (i=0; i<nBlockSwitchWindows; i++) {
278        FIXP_DBL tmp = fMultDiv2(oneMinusAccWindowNrgFac, blockSwitchingControl->accWindowNrg);
279        blockSwitchingControl->accWindowNrg = fMultAdd(tmp, accWindowNrgFac, enM1) ;
280
281        if (fMult(blockSwitchingControl->windowNrgF[1][i],invAttackRatio) > blockSwitchingControl->accWindowNrg ) {
282            blockSwitchingControl->attack = TRUE;
283            blockSwitchingControl->attackIndex = i;
284        }
285        enM1 = blockSwitchingControl->windowNrgF[1][i];
286        enMax = fixMax(enMax, enM1);
287    }
288
289
290    if (enMax < minAttackNrg) blockSwitchingControl->attack = FALSE;
291
292    /* Check if attack spreads over frame border */
293    if((blockSwitchingControl->attack == FALSE) && (blockSwitchingControl->lastattack == TRUE)) {
294        /* if attack is in last window repeat SHORT_WINDOW */
295        if ( ((blockSwitchingControl->windowNrgF[0][nBlockSwitchWindows-1]>>4) > fMult((FIXP_DBL)(10<<(DFRACT_BITS-1-4)), blockSwitchingControl->windowNrgF[1][1]))
296           && (blockSwitchingControl->lastAttackIndex == (INT)nBlockSwitchWindows-1)
297        )
298        {
299            blockSwitchingControl->attack = TRUE;
300            blockSwitchingControl->attackIndex = 0;
301        }
302    }
303
304
305    if(blockSwitchingControl->allowLookAhead)
306    {
307
308
309      blockSwitchingControl->lastWindowSequence =
310        chgWndSqLkAhd[blockSwitchingControl->lastattack][blockSwitchingControl->attack][blockSwitchingControl->lastWindowSequence];
311    }
312    else
313    {
314      /* Low Delay */
315      blockSwitchingControl->lastWindowSequence =
316        chgWndSq[blockSwitchingControl->attack][blockSwitchingControl->lastWindowSequence];
317    }
318
319
320    /* update window shape */
321    blockSwitchingControl->windowShape = blockType2windowShape[blockSwitchingControl->allowShortFrames][blockSwitchingControl->lastWindowSequence];
322
323    return(0);
324}
325
326
327
328static FIXP_DBL FDKaacEnc_GetWindowEnergy(const FIXP_DBL in[], const INT blSwWndIdx)
329{
330/* For coherency, change FDKaacEnc_GetWindowEnergy() to calcluate the energy for a block switching analysis windows,
331   not for a short block. The same is done FDKaacEnc_CalcWindowEnergy(). The result of FDKaacEnc_GetWindowEnergy()
332   is used for a comparision of the max energy of left/right channel. */
333
334  return in[blSwWndIdx];
335
336}
337
338
339static void FDKaacEnc_CalcWindowEnergy(BLOCK_SWITCHING_CONTROL *RESTRICT blockSwitchingControl, INT windowLen)
340{
341    INT  i;
342    UINT w;
343
344    FIXP_SGL hiPassCoeff0 = hiPassCoeff[0];
345    FIXP_SGL hiPassCoeff1 = hiPassCoeff[1];
346
347    INT_PCM *timeSignal = blockSwitchingControl->timeSignal;
348
349    /* sum up scalarproduct of timesignal as windowed Energies */
350    for (w=0; w < blockSwitchingControl->nBlockSwitchWindows; w++) {
351
352        FIXP_DBL temp_windowNrg  = FL2FXCONST_DBL(0.0f);
353        FIXP_DBL temp_windowNrgF = FL2FXCONST_DBL(0.0f);
354        FIXP_DBL temp_iirState0  = blockSwitchingControl->iirStates[0];
355        FIXP_DBL temp_iirState1  = blockSwitchingControl->iirStates[1];
356
357        /* windowNrg = sum(timesample^2) */
358        for(i=0;i<windowLen;i++)
359        {
360
361            FIXP_DBL tempUnfiltered, tempFiltred, t1, t2;
362            /* tempUnfiltered is scaled with 1 to prevent overflows during calculation of tempFiltred */
363#if SAMPLE_BITS == DFRACT_BITS
364            tempUnfiltered = (FIXP_DBL) *timeSignal++ >> 1;
365#else
366            tempUnfiltered = (FIXP_DBL) *timeSignal++ << (DFRACT_BITS-SAMPLE_BITS-1);
367#endif
368            t1 = fMultDiv2(hiPassCoeff1, tempUnfiltered-temp_iirState0);
369            t2 = fMultDiv2(hiPassCoeff0, temp_iirState1);
370            tempFiltred = (t1 - t2) << 1;
371
372            temp_iirState0 = tempUnfiltered;
373            temp_iirState1 = tempFiltred;
374
375            /* subtract 2 from overallscaling (BLOCK_SWITCH_ENERGY_SHIFT)
376             * because tempUnfiltered was already scaled with 1 (is 2 after squaring)
377             * subtract 1 from overallscaling (BLOCK_SWITCH_ENERGY_SHIFT)
378             * because of fMultDiv2 is doing a scaling by one */
379            temp_windowNrg += fPow2Div2(tempUnfiltered) >> (BLOCK_SWITCH_ENERGY_SHIFT - 1 - 2);
380            temp_windowNrgF += fPow2Div2(tempFiltred) >> (BLOCK_SWITCH_ENERGY_SHIFT - 1 - 2);
381        }
382        blockSwitchingControl->windowNrg[1][w]  = temp_windowNrg;
383        blockSwitchingControl->windowNrgF[1][w] = temp_windowNrgF;
384        blockSwitchingControl->iirStates[0]     = temp_iirState0;
385        blockSwitchingControl->iirStates[1]     = temp_iirState1;
386    }
387}
388
389
390static const UCHAR synchronizedBlockTypeTable[5][5] =
391{
392  /*                  LONG_WINDOW   START_WINDOW  SHORT_WINDOW  STOP_WINDOW   LOWOV_WINDOW*/
393  /* LONG_WINDOW  */ {LONG_WINDOW,  START_WINDOW, SHORT_WINDOW, STOP_WINDOW,  LOWOV_WINDOW},
394  /* START_WINDOW */ {START_WINDOW, START_WINDOW, SHORT_WINDOW, SHORT_WINDOW, LOWOV_WINDOW},
395  /* SHORT_WINDOW */ {SHORT_WINDOW, SHORT_WINDOW, SHORT_WINDOW, SHORT_WINDOW, WRONG_WINDOW},
396  /* STOP_WINDOW  */ {STOP_WINDOW,  SHORT_WINDOW, SHORT_WINDOW, STOP_WINDOW,  LOWOV_WINDOW},
397  /* LOWOV_WINDOW */ {LOWOV_WINDOW, LOWOV_WINDOW, WRONG_WINDOW, LOWOV_WINDOW, LOWOV_WINDOW},
398};
399
400int FDKaacEnc_SyncBlockSwitching (
401      BLOCK_SWITCHING_CONTROL *blockSwitchingControlLeft,
402      BLOCK_SWITCHING_CONTROL *blockSwitchingControlRight,
403      const INT nChannels,
404      const INT commonWindow )
405{
406  UCHAR patchType = LONG_WINDOW;
407
408  if( nChannels == 2 && commonWindow == TRUE)
409  {
410    /* could be better with a channel loop (need a handle to psy_data) */
411    /* get suggested Block Types and synchronize */
412    patchType = synchronizedBlockTypeTable[patchType][blockSwitchingControlLeft->lastWindowSequence];
413    patchType = synchronizedBlockTypeTable[patchType][blockSwitchingControlRight->lastWindowSequence];
414
415    /* sanity check (no change from low overlap window to short winow and vice versa) */
416    if (patchType == WRONG_WINDOW)
417      return -1; /* mixed up AAC-LC and AAC-LD */
418
419    /* Set synchronized Blocktype */
420    blockSwitchingControlLeft->lastWindowSequence  = patchType;
421    blockSwitchingControlRight->lastWindowSequence = patchType;
422
423    /* update window shape */
424    blockSwitchingControlLeft->windowShape  = blockType2windowShape[blockSwitchingControlLeft->allowShortFrames][blockSwitchingControlLeft->lastWindowSequence];
425    blockSwitchingControlRight->windowShape = blockType2windowShape[blockSwitchingControlLeft->allowShortFrames][blockSwitchingControlRight->lastWindowSequence];
426  }
427
428  if (blockSwitchingControlLeft->allowShortFrames)
429  {
430    int i;
431
432    if( nChannels == 2 )
433    {
434      if (commonWindow == TRUE)
435      {
436        /* Synchronize grouping info */
437        int windowSequenceLeftOld  = blockSwitchingControlLeft->lastWindowSequence;
438        int windowSequenceRightOld = blockSwitchingControlRight->lastWindowSequence;
439
440        /* Long Blocks */
441        if(patchType != SHORT_WINDOW) {
442          /* Set grouping info */
443          blockSwitchingControlLeft->noOfGroups   = 1;
444          blockSwitchingControlRight->noOfGroups  = 1;
445          blockSwitchingControlLeft->groupLen[0]  = 1;
446          blockSwitchingControlRight->groupLen[0] = 1;
447
448          for (i = 1; i < MAX_NO_OF_GROUPS; i++)
449          {
450            blockSwitchingControlLeft->groupLen[i]  = 0;
451            blockSwitchingControlRight->groupLen[i] = 0;
452          }
453        }
454
455        /* Short Blocks */
456        else {
457          /* in case all two channels were detected as short-blocks before syncing, use the grouping of channel with higher maxWindowNrg */
458          if( (windowSequenceLeftOld  == SHORT_WINDOW) &&
459	            (windowSequenceRightOld == SHORT_WINDOW) )
460          {
461            if(blockSwitchingControlLeft->maxWindowNrg > blockSwitchingControlRight->maxWindowNrg) {
462	            /* Left Channel wins */
463	            blockSwitchingControlRight->noOfGroups = blockSwitchingControlLeft->noOfGroups;
464	            for (i = 0; i < MAX_NO_OF_GROUPS; i++){
465	              blockSwitchingControlRight->groupLen[i] = blockSwitchingControlLeft->groupLen[i];
466	            }
467            }
468            else {
469	            /* Right Channel wins */
470	            blockSwitchingControlLeft->noOfGroups = blockSwitchingControlRight->noOfGroups;
471	            for (i = 0; i < MAX_NO_OF_GROUPS; i++){
472	              blockSwitchingControlLeft->groupLen[i] = blockSwitchingControlRight->groupLen[i];
473	            }
474            }
475          }
476          else if ( (windowSequenceLeftOld  == SHORT_WINDOW) &&
477                    (windowSequenceRightOld != SHORT_WINDOW) )
478          {
479            /* else use grouping of short-block channel */
480            blockSwitchingControlRight->noOfGroups = blockSwitchingControlLeft->noOfGroups;
481            for (i = 0; i < MAX_NO_OF_GROUPS; i++){
482              blockSwitchingControlRight->groupLen[i] = blockSwitchingControlLeft->groupLen[i];
483            }
484          }
485          else if ( (windowSequenceRightOld == SHORT_WINDOW) &&
486		                (windowSequenceLeftOld  != SHORT_WINDOW) )
487          {
488            blockSwitchingControlLeft->noOfGroups = blockSwitchingControlRight->noOfGroups;
489            for (i = 0; i < MAX_NO_OF_GROUPS; i++){
490              blockSwitchingControlLeft->groupLen[i] = blockSwitchingControlRight->groupLen[i];
491            }
492          } else {
493            /* syncing a start and stop window ... */
494            blockSwitchingControlLeft->noOfGroups  = blockSwitchingControlRight->noOfGroups  = 2;
495            blockSwitchingControlLeft->groupLen[0] = blockSwitchingControlRight->groupLen[0] = 4;
496            blockSwitchingControlLeft->groupLen[1] = blockSwitchingControlRight->groupLen[1] = 4;
497          }
498        } /* Short Blocks */
499      }
500      else {
501        /* stereo, no common window */
502        if (blockSwitchingControlLeft->lastWindowSequence!=SHORT_WINDOW){
503          blockSwitchingControlLeft->noOfGroups  = 1;
504          blockSwitchingControlLeft->groupLen[0] = 1;
505          for (i = 1; i < MAX_NO_OF_GROUPS; i++)
506          {
507            blockSwitchingControlLeft->groupLen[i] = 0;
508          }
509        }
510        if (blockSwitchingControlRight->lastWindowSequence!=SHORT_WINDOW){
511          blockSwitchingControlRight->noOfGroups  = 1;
512          blockSwitchingControlRight->groupLen[0] = 1;
513          for (i = 1; i < MAX_NO_OF_GROUPS; i++)
514          {
515            blockSwitchingControlRight->groupLen[i] = 0;
516          }
517        }
518      } /* common window */
519    } else {
520      /* Mono */
521      if (blockSwitchingControlLeft->lastWindowSequence!=SHORT_WINDOW){
522        blockSwitchingControlLeft->noOfGroups  = 1;
523        blockSwitchingControlLeft->groupLen[0] = 1;
524
525        for (i = 1; i < MAX_NO_OF_GROUPS; i++)
526        {
527          blockSwitchingControlLeft->groupLen[i] = 0;
528        }
529      }
530    }
531  } /* allowShortFrames */
532
533
534  /* Translate LOWOV_WINDOW block type to a meaningful window shape. */
535  if ( ! blockSwitchingControlLeft->allowShortFrames ) {
536    if ( blockSwitchingControlLeft->lastWindowSequence != LONG_WINDOW
537      && blockSwitchingControlLeft->lastWindowSequence != STOP_WINDOW )
538    {
539      blockSwitchingControlLeft->lastWindowSequence = LONG_WINDOW;
540      blockSwitchingControlLeft->windowShape = LOL_WINDOW;
541    }
542  }
543  if (nChannels == 2) {
544    if ( ! blockSwitchingControlRight->allowShortFrames ) {
545      if ( blockSwitchingControlRight->lastWindowSequence != LONG_WINDOW
546        && blockSwitchingControlRight->lastWindowSequence != STOP_WINDOW )
547      {
548        blockSwitchingControlRight->lastWindowSequence = LONG_WINDOW;
549        blockSwitchingControlRight->windowShape = LOL_WINDOW;
550      }
551    }
552  }
553
554  return 0;
555}
556
557
558