Home | History | Annotate | Download | only in src
      1 /* -----------------------------------------------------------------------------
      2 Software License for The Fraunhofer FDK AAC Codec Library for Android
      3 
      4  Copyright  1995 - 2018 Fraunhofer-Gesellschaft zur Frderung der angewandten
      5 Forschung e.V. All rights reserved.
      6 
      7  1.    INTRODUCTION
      8 The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software
      9 that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding
     10 scheme for digital audio. This FDK AAC Codec software is intended to be used on
     11 a wide variety of Android devices.
     12 
     13 AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient
     14 general perceptual audio codecs. AAC-ELD is considered the best-performing
     15 full-bandwidth communications codec by independent studies and is widely
     16 deployed. AAC has been standardized by ISO and IEC as part of the MPEG
     17 specifications.
     18 
     19 Patent licenses for necessary patent claims for the FDK AAC Codec (including
     20 those of Fraunhofer) may be obtained through Via Licensing
     21 (www.vialicensing.com) or through the respective patent owners individually for
     22 the purpose of encoding or decoding bit streams in products that are compliant
     23 with the ISO/IEC MPEG audio standards. Please note that most manufacturers of
     24 Android devices already license these patent claims through Via Licensing or
     25 directly from the patent owners, and therefore FDK AAC Codec software may
     26 already be covered under those patent licenses when it is used for those
     27 licensed purposes only.
     28 
     29 Commercially-licensed AAC software libraries, including floating-point versions
     30 with enhanced sound quality, are also available from Fraunhofer. Users are
     31 encouraged to check the Fraunhofer website for additional applications
     32 information and documentation.
     33 
     34 2.    COPYRIGHT LICENSE
     35 
     36 Redistribution and use in source and binary forms, with or without modification,
     37 are permitted without payment of copyright license fees provided that you
     38 satisfy the following conditions:
     39 
     40 You must retain the complete text of this software license in redistributions of
     41 the FDK AAC Codec or your modifications thereto in source code form.
     42 
     43 You must retain the complete text of this software license in the documentation
     44 and/or other materials provided with redistributions of the FDK AAC Codec or
     45 your modifications thereto in binary form. You must make available free of
     46 charge copies of the complete source code of the FDK AAC Codec and your
     47 modifications thereto to recipients of copies in binary form.
     48 
     49 The name of Fraunhofer may not be used to endorse or promote products derived
     50 from this library without prior written permission.
     51 
     52 You may not charge copyright license fees for anyone to use, copy or distribute
     53 the FDK AAC Codec software or your modifications thereto.
     54 
     55 Your modified versions of the FDK AAC Codec must carry prominent notices stating
     56 that you changed the software and the date of any change. For modified versions
     57 of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android"
     58 must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK
     59 AAC Codec Library for Android."
     60 
     61 3.    NO PATENT LICENSE
     62 
     63 NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without
     64 limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE.
     65 Fraunhofer provides no warranty of patent non-infringement with respect to this
     66 software.
     67 
     68 You may use this FDK AAC Codec software or modifications thereto only for
     69 purposes that are authorized by appropriate patent licenses.
     70 
     71 4.    DISCLAIMER
     72 
     73 This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright
     74 holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
     75 including but not limited to the implied warranties of merchantability and
     76 fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
     77 CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary,
     78 or consequential damages, including but not limited to procurement of substitute
     79 goods or services; loss of use, data, or profits, or business interruption,
     80 however caused and on any theory of liability, whether in contract, strict
     81 liability, or tort (including negligence), arising in any way out of the use of
     82 this software, even if advised of the possibility of such damage.
     83 
     84 5.    CONTACT INFORMATION
     85 
     86 Fraunhofer Institute for Integrated Circuits IIS
     87 Attention: Audio and Multimedia Departments - FDK AAC LL
     88 Am Wolfsmantel 33
     89 91058 Erlangen, Germany
     90 
     91 www.iis.fraunhofer.de/amm
     92 amm-info (at) iis.fraunhofer.de
     93 ----------------------------------------------------------------------------- */
     94 
     95 /**************************** PCM utility library ******************************
     96 
     97    Author(s):   Matthias Neusinger
     98 
     99    Description: Hard limiter for clipping prevention
    100 
    101 *******************************************************************************/
    102 
    103 #include "limiter.h"
    104 #include "FDK_core.h"
    105 
    106 /* library version */
    107 #include "version.h"
    108 /* library title */
    109 #define TDLIMIT_LIB_TITLE "TD Limiter Lib"
    110 
    111 /* create limiter */
    112 TDLimiterPtr pcmLimiter_Create(unsigned int maxAttackMs, unsigned int releaseMs,
    113                                FIXP_DBL threshold, unsigned int maxChannels,
    114                                UINT maxSampleRate) {
    115   TDLimiterPtr limiter = NULL;
    116   unsigned int attack, release;
    117   FIXP_DBL attackConst, releaseConst, exponent;
    118   INT e_ans;
    119 
    120   /* calc attack and release time in samples */
    121   attack = (unsigned int)(maxAttackMs * maxSampleRate / 1000);
    122   release = (unsigned int)(releaseMs * maxSampleRate / 1000);
    123 
    124   /* alloc limiter struct */
    125   limiter = (TDLimiterPtr)FDKcalloc(1, sizeof(struct TDLimiter));
    126   if (!limiter) return NULL;
    127 
    128   /* alloc max and delay buffers */
    129   limiter->maxBuf = (FIXP_DBL*)FDKcalloc(attack + 1, sizeof(FIXP_DBL));
    130   limiter->delayBuf =
    131       (FIXP_DBL*)FDKcalloc(attack * maxChannels, sizeof(FIXP_DBL));
    132 
    133   if (!limiter->maxBuf || !limiter->delayBuf) {
    134     pcmLimiter_Destroy(limiter);
    135     return NULL;
    136   }
    137 
    138   /* attackConst = pow(0.1, 1.0 / (attack + 1)) */
    139   exponent = invFixp(attack + 1);
    140   attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
    141   attackConst = scaleValue(attackConst, e_ans);
    142 
    143   /* releaseConst  = (float)pow(0.1, 1.0 / (release + 1)) */
    144   exponent = invFixp(release + 1);
    145   releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
    146   releaseConst = scaleValue(releaseConst, e_ans);
    147 
    148   /* init parameters */
    149   limiter->attackMs = maxAttackMs;
    150   limiter->maxAttackMs = maxAttackMs;
    151   limiter->releaseMs = releaseMs;
    152   limiter->attack = attack;
    153   limiter->attackConst = attackConst;
    154   limiter->releaseConst = releaseConst;
    155   limiter->threshold = threshold >> TDL_GAIN_SCALING;
    156   limiter->channels = maxChannels;
    157   limiter->maxChannels = maxChannels;
    158   limiter->sampleRate = maxSampleRate;
    159   limiter->maxSampleRate = maxSampleRate;
    160 
    161   pcmLimiter_Reset(limiter);
    162 
    163   return limiter;
    164 }
    165 
    166 /* apply limiter */
    167 TDLIMITER_ERROR pcmLimiter_Apply(TDLimiterPtr limiter, PCM_LIM* samplesIn,
    168                                  INT_PCM* samplesOut, FIXP_DBL* RESTRICT pGain,
    169                                  const INT* RESTRICT gain_scale,
    170                                  const UINT gain_size, const UINT gain_delay,
    171                                  const UINT nSamples) {
    172   unsigned int i, j;
    173   FIXP_DBL tmp1;
    174   FIXP_DBL tmp2;
    175   FIXP_DBL tmp, old, gain, additionalGain = 0, additionalGainUnfiltered;
    176   FIXP_DBL minGain = FL2FXCONST_DBL(1.0f / (1 << 1));
    177 
    178   FDK_ASSERT(gain_size == 1);
    179   FDK_ASSERT(gain_delay <= nSamples);
    180 
    181   if (limiter == NULL) return TDLIMIT_INVALID_HANDLE;
    182 
    183   {
    184     unsigned int channels = limiter->channels;
    185     unsigned int attack = limiter->attack;
    186     FIXP_DBL attackConst = limiter->attackConst;
    187     FIXP_DBL releaseConst = limiter->releaseConst;
    188     FIXP_DBL threshold = limiter->threshold;
    189 
    190     FIXP_DBL max = limiter->max;
    191     FIXP_DBL* maxBuf = limiter->maxBuf;
    192     unsigned int maxBufIdx = limiter->maxBufIdx;
    193     FIXP_DBL cor = limiter->cor;
    194     FIXP_DBL* delayBuf = limiter->delayBuf;
    195     unsigned int delayBufIdx = limiter->delayBufIdx;
    196 
    197     FIXP_DBL smoothState0 = limiter->smoothState0;
    198     FIXP_DBL additionalGainSmoothState = limiter->additionalGainFilterState;
    199     FIXP_DBL additionalGainSmoothState1 = limiter->additionalGainFilterState1;
    200 
    201     if (!gain_delay) {
    202       additionalGain = pGain[0];
    203       if (gain_scale[0] > 0) {
    204         additionalGain <<= gain_scale[0];
    205       } else {
    206         additionalGain >>= -gain_scale[0];
    207       }
    208     }
    209 
    210     for (i = 0; i < nSamples; i++) {
    211       if (gain_delay) {
    212         if (i < gain_delay) {
    213           additionalGainUnfiltered = limiter->additionalGainPrev;
    214         } else {
    215           additionalGainUnfiltered = pGain[0];
    216         }
    217 
    218         /* Smooth additionalGain */
    219         /* [b,a] = butter(1, 0.01) */
    220         static const FIXP_SGL b[] = {FL2FXCONST_SGL(0.015466 * 2.0),
    221                                      FL2FXCONST_SGL(0.015466 * 2.0)};
    222         static const FIXP_SGL a[] = {(FIXP_SGL)MAXVAL_SGL,
    223                                      FL2FXCONST_SGL(-0.96907)};
    224         additionalGain = -fMult(additionalGainSmoothState, a[1]) +
    225                          fMultDiv2(additionalGainUnfiltered, b[0]) +
    226                          fMultDiv2(additionalGainSmoothState1, b[1]);
    227         additionalGainSmoothState1 = additionalGainUnfiltered;
    228         additionalGainSmoothState = additionalGain;
    229 
    230         /* Apply the additional scaling that has no delay and no smoothing */
    231         if (gain_scale[0] > 0) {
    232           additionalGain <<= gain_scale[0];
    233         } else {
    234           additionalGain >>= -gain_scale[0];
    235         }
    236       }
    237       /* get maximum absolute sample value of all channels, including the
    238        * additional gain. */
    239       tmp1 = (FIXP_DBL)0;
    240       for (j = 0; j < channels; j++) {
    241         tmp2 = PCM_LIM2FIXP_DBL(samplesIn[j]);
    242         tmp2 = fAbs(tmp2);
    243         tmp2 = FIXP_DBL(INT(tmp2) ^ INT((tmp2 >> (SAMPLE_BITS_LIM - 1))));
    244         tmp1 = fMax(tmp1, tmp2);
    245       }
    246       tmp = fMult(tmp1, additionalGain);
    247 
    248       /* set threshold as lower border to save calculations in running maximum
    249        * algorithm */
    250       tmp = fMax(tmp, threshold);
    251 
    252       /* running maximum */
    253       old = maxBuf[maxBufIdx];
    254       maxBuf[maxBufIdx] = tmp;
    255 
    256       if (tmp >= max) {
    257         /* new sample is greater than old maximum, so it is the new maximum */
    258         max = tmp;
    259       } else if (old < max) {
    260         /* maximum does not change, as the sample, which has left the window was
    261            not the maximum */
    262       } else {
    263         /* the old maximum has left the window, we have to search the complete
    264            buffer for the new max */
    265         max = maxBuf[0];
    266         for (j = 1; j <= attack; j++) {
    267           max = fMax(max, maxBuf[j]);
    268         }
    269       }
    270       maxBufIdx++;
    271       if (maxBufIdx >= attack + 1) maxBufIdx = 0;
    272 
    273       /* calc gain */
    274       /* gain is downscaled by one, so that gain = 1.0 can be represented */
    275       if (max > threshold) {
    276         gain = fDivNorm(threshold, max) >> 1;
    277       } else {
    278         gain = FL2FXCONST_DBL(1.0f / (1 << 1));
    279       }
    280 
    281       /* gain smoothing, method: TDL_EXPONENTIAL */
    282       /* first order IIR filter with attack correction to avoid overshoots */
    283 
    284       /* correct the 'aiming' value of the exponential attack to avoid the
    285        * remaining overshoot */
    286       if (gain < smoothState0) {
    287         cor = fMin(cor,
    288                    fMultDiv2((gain - fMultDiv2(FL2FXCONST_SGL(0.1f * (1 << 1)),
    289                                                smoothState0)),
    290                              FL2FXCONST_SGL(1.11111111f / (1 << 1)))
    291                        << 2);
    292       } else {
    293         cor = gain;
    294       }
    295 
    296       /* smoothing filter */
    297       if (cor < smoothState0) {
    298         smoothState0 =
    299             fMult(attackConst, (smoothState0 - cor)) + cor; /* attack */
    300         smoothState0 = fMax(smoothState0, gain); /* avoid overshooting target */
    301       } else {
    302         /* sign inversion twice to round towards +infinity,
    303            so that gain can converge to 1.0 again,
    304            for bit-identical output when limiter is not active */
    305         smoothState0 =
    306             -fMult(releaseConst, -(smoothState0 - cor)) + cor; /* release */
    307       }
    308 
    309       gain = smoothState0;
    310 
    311       FIXP_DBL* p_delayBuf = &delayBuf[delayBufIdx * channels + 0];
    312       if (gain < FL2FXCONST_DBL(1.0f / (1 << 1))) {
    313         gain <<= 1;
    314         /* lookahead delay, apply gain */
    315         for (j = 0; j < channels; j++) {
    316           tmp = p_delayBuf[j];
    317           p_delayBuf[j] = fMult((FIXP_PCM_LIM)samplesIn[j], additionalGain);
    318 
    319           /* Apply gain to delayed signal */
    320           tmp = fMultDiv2(tmp, gain);
    321 
    322           samplesOut[j] = (INT_PCM)FX_DBL2FX_PCM((FIXP_DBL)SATURATE_LEFT_SHIFT(
    323               tmp, TDL_GAIN_SCALING + 1, DFRACT_BITS));
    324         }
    325         gain >>= 1;
    326       } else {
    327         /* lookahead delay, apply gain=1.0f */
    328         for (j = 0; j < channels; j++) {
    329           tmp = p_delayBuf[j];
    330           p_delayBuf[j] = fMult((FIXP_PCM_LIM)samplesIn[j], additionalGain);
    331           samplesOut[j] = (INT_PCM)FX_DBL2FX_PCM((FIXP_DBL)SATURATE_LEFT_SHIFT(
    332               tmp, TDL_GAIN_SCALING, DFRACT_BITS));
    333         }
    334       }
    335 
    336       delayBufIdx++;
    337       if (delayBufIdx >= attack) {
    338         delayBufIdx = 0;
    339       }
    340 
    341       /* save minimum gain factor */
    342       if (gain < minGain) {
    343         minGain = gain;
    344       }
    345 
    346       /* advance sample pointer by <channel> samples */
    347       samplesIn += channels;
    348       samplesOut += channels;
    349     }
    350 
    351     limiter->max = max;
    352     limiter->maxBufIdx = maxBufIdx;
    353     limiter->cor = cor;
    354     limiter->delayBufIdx = delayBufIdx;
    355 
    356     limiter->smoothState0 = smoothState0;
    357     limiter->additionalGainFilterState = additionalGainSmoothState;
    358     limiter->additionalGainFilterState1 = additionalGainSmoothState1;
    359 
    360     limiter->minGain = minGain;
    361 
    362     limiter->additionalGainPrev = pGain[0];
    363 
    364     return TDLIMIT_OK;
    365   }
    366 }
    367 
    368 /* set limiter threshold */
    369 TDLIMITER_ERROR pcmLimiter_SetThreshold(TDLimiterPtr limiter,
    370                                         FIXP_DBL threshold) {
    371   if (limiter == NULL) return TDLIMIT_INVALID_HANDLE;
    372 
    373   limiter->threshold = threshold >> TDL_GAIN_SCALING;
    374 
    375   return TDLIMIT_OK;
    376 }
    377 
    378 /* reset limiter */
    379 TDLIMITER_ERROR pcmLimiter_Reset(TDLimiterPtr limiter) {
    380   if (limiter != NULL) {
    381     limiter->maxBufIdx = 0;
    382     limiter->delayBufIdx = 0;
    383     limiter->max = (FIXP_DBL)0;
    384     limiter->cor = FL2FXCONST_DBL(1.0f / (1 << 1));
    385     limiter->smoothState0 = FL2FXCONST_DBL(1.0f / (1 << 1));
    386     limiter->minGain = FL2FXCONST_DBL(1.0f / (1 << 1));
    387 
    388     limiter->additionalGainPrev =
    389         FL2FXCONST_DBL(1.0f / (1 << TDL_GAIN_SCALING));
    390     limiter->additionalGainFilterState =
    391         FL2FXCONST_DBL(1.0f / (1 << TDL_GAIN_SCALING));
    392     limiter->additionalGainFilterState1 =
    393         FL2FXCONST_DBL(1.0f / (1 << TDL_GAIN_SCALING));
    394 
    395     FDKmemset(limiter->maxBuf, 0, (limiter->attack + 1) * sizeof(FIXP_DBL));
    396     FDKmemset(limiter->delayBuf, 0,
    397               limiter->attack * limiter->channels * sizeof(FIXP_DBL));
    398   } else {
    399     return TDLIMIT_INVALID_HANDLE;
    400   }
    401 
    402   return TDLIMIT_OK;
    403 }
    404 
    405 /* destroy limiter */
    406 TDLIMITER_ERROR pcmLimiter_Destroy(TDLimiterPtr limiter) {
    407   if (limiter != NULL) {
    408     FDKfree(limiter->maxBuf);
    409     FDKfree(limiter->delayBuf);
    410 
    411     FDKfree(limiter);
    412   } else {
    413     return TDLIMIT_INVALID_HANDLE;
    414   }
    415   return TDLIMIT_OK;
    416 }
    417 
    418 /* get delay in samples */
    419 unsigned int pcmLimiter_GetDelay(TDLimiterPtr limiter) {
    420   FDK_ASSERT(limiter != NULL);
    421   return limiter->attack;
    422 }
    423 
    424 /* get maximum gain reduction of last processed block */
    425 INT pcmLimiter_GetMaxGainReduction(TDLimiterPtr limiter) {
    426   /* maximum gain reduction in dB = -20 * log10(limiter->minGain)
    427      = -20 * log2(limiter->minGain)/log2(10) = -6.0206*log2(limiter->minGain) */
    428   int e_ans;
    429   FIXP_DBL loggain, maxGainReduction;
    430 
    431   FDK_ASSERT(limiter != NULL);
    432 
    433   loggain = fLog2(limiter->minGain, 1, &e_ans);
    434 
    435   maxGainReduction = fMult(loggain, FL2FXCONST_DBL(-6.0206f / (1 << 3)));
    436 
    437   return fixp_roundToInt(maxGainReduction, (e_ans + 3));
    438 }
    439 
    440 /* set number of channels */
    441 TDLIMITER_ERROR pcmLimiter_SetNChannels(TDLimiterPtr limiter,
    442                                         unsigned int nChannels) {
    443   if (limiter == NULL) return TDLIMIT_INVALID_HANDLE;
    444 
    445   if (nChannels > limiter->maxChannels) return TDLIMIT_INVALID_PARAMETER;
    446 
    447   limiter->channels = nChannels;
    448   // pcmLimiter_Reset(limiter);
    449 
    450   return TDLIMIT_OK;
    451 }
    452 
    453 /* set sampling rate */
    454 TDLIMITER_ERROR pcmLimiter_SetSampleRate(TDLimiterPtr limiter,
    455                                          UINT sampleRate) {
    456   unsigned int attack, release;
    457   FIXP_DBL attackConst, releaseConst, exponent;
    458   INT e_ans;
    459 
    460   if (limiter == NULL) return TDLIMIT_INVALID_HANDLE;
    461 
    462   if (sampleRate > limiter->maxSampleRate) return TDLIMIT_INVALID_PARAMETER;
    463 
    464   /* update attack and release time in samples */
    465   attack = (unsigned int)(limiter->attackMs * sampleRate / 1000);
    466   release = (unsigned int)(limiter->releaseMs * sampleRate / 1000);
    467 
    468   /* attackConst = pow(0.1, 1.0 / (attack + 1)) */
    469   exponent = invFixp(attack + 1);
    470   attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
    471   attackConst = scaleValue(attackConst, e_ans);
    472 
    473   /* releaseConst  = (float)pow(0.1, 1.0 / (release + 1)) */
    474   exponent = invFixp(release + 1);
    475   releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
    476   releaseConst = scaleValue(releaseConst, e_ans);
    477 
    478   limiter->attack = attack;
    479   limiter->attackConst = attackConst;
    480   limiter->releaseConst = releaseConst;
    481   limiter->sampleRate = sampleRate;
    482 
    483   /* reset */
    484   // pcmLimiter_Reset(limiter);
    485 
    486   return TDLIMIT_OK;
    487 }
    488 
    489 /* set attack time */
    490 TDLIMITER_ERROR pcmLimiter_SetAttack(TDLimiterPtr limiter,
    491                                      unsigned int attackMs) {
    492   unsigned int attack;
    493   FIXP_DBL attackConst, exponent;
    494   INT e_ans;
    495 
    496   if (limiter == NULL) return TDLIMIT_INVALID_HANDLE;
    497 
    498   if (attackMs > limiter->maxAttackMs) return TDLIMIT_INVALID_PARAMETER;
    499 
    500   /* calculate attack time in samples */
    501   attack = (unsigned int)(attackMs * limiter->sampleRate / 1000);
    502 
    503   /* attackConst = pow(0.1, 1.0 / (attack + 1)) */
    504   exponent = invFixp(attack + 1);
    505   attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
    506   attackConst = scaleValue(attackConst, e_ans);
    507 
    508   limiter->attack = attack;
    509   limiter->attackConst = attackConst;
    510   limiter->attackMs = attackMs;
    511 
    512   return TDLIMIT_OK;
    513 }
    514 
    515 /* set release time */
    516 TDLIMITER_ERROR pcmLimiter_SetRelease(TDLimiterPtr limiter,
    517                                       unsigned int releaseMs) {
    518   unsigned int release;
    519   FIXP_DBL releaseConst, exponent;
    520   INT e_ans;
    521 
    522   if (limiter == NULL) return TDLIMIT_INVALID_HANDLE;
    523 
    524   /* calculate  release time in samples */
    525   release = (unsigned int)(releaseMs * limiter->sampleRate / 1000);
    526 
    527   /* releaseConst  = (float)pow(0.1, 1.0 / (release + 1)) */
    528   exponent = invFixp(release + 1);
    529   releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans);
    530   releaseConst = scaleValue(releaseConst, e_ans);
    531 
    532   limiter->releaseConst = releaseConst;
    533   limiter->releaseMs = releaseMs;
    534 
    535   return TDLIMIT_OK;
    536 }
    537 
    538 /* Get library info for this module. */
    539 TDLIMITER_ERROR pcmLimiter_GetLibInfo(LIB_INFO* info) {
    540   int i;
    541 
    542   if (info == NULL) {
    543     return TDLIMIT_INVALID_PARAMETER;
    544   }
    545 
    546   /* Search for next free tab */
    547   for (i = 0; i < FDK_MODULE_LAST; i++) {
    548     if (info[i].module_id == FDK_NONE) break;
    549   }
    550   if (i == FDK_MODULE_LAST) {
    551     return TDLIMIT_UNKNOWN;
    552   }
    553 
    554   /* Add the library info */
    555   info[i].module_id = FDK_TDLIMIT;
    556   info[i].version =
    557       LIB_VERSION(PCMUTIL_LIB_VL0, PCMUTIL_LIB_VL1, PCMUTIL_LIB_VL2);
    558   LIB_VERSION_STRING(info + i);
    559   info[i].build_date = PCMUTIL_LIB_BUILD_DATE;
    560   info[i].build_time = PCMUTIL_LIB_BUILD_TIME;
    561   info[i].title = TDLIMIT_LIB_TITLE;
    562 
    563   /* Set flags */
    564   info[i].flags = CAPF_LIMITER;
    565 
    566   /* Add lib info for FDK tools (if not yet done). */
    567   FDK_toolsGetLibInfo(info);
    568 
    569   return TDLIMIT_OK;
    570 }
    571