• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -----------------------------------------------------------------------------
2 Software License for The Fraunhofer FDK AAC Codec Library for Android
3 
4 © Copyright  1995 - 2018 Fraunhofer-Gesellschaft zur Förderung der angewandten
5 Forschung e.V. All rights reserved.
6 
7  1.    INTRODUCTION
8 The Fraunhofer FDK AAC Codec Library for Android ("FDK AAC Codec") is software
9 that implements the MPEG Advanced Audio Coding ("AAC") encoding and decoding
10 scheme for digital audio. This FDK AAC Codec software is intended to be used on
11 a wide variety of Android devices.
12 
13 AAC's HE-AAC and HE-AAC v2 versions are regarded as today's most efficient
14 general perceptual audio codecs. AAC-ELD is considered the best-performing
15 full-bandwidth communications codec by independent studies and is widely
16 deployed. AAC has been standardized by ISO and IEC as part of the MPEG
17 specifications.
18 
19 Patent licenses for necessary patent claims for the FDK AAC Codec (including
20 those of Fraunhofer) may be obtained through Via Licensing
21 (www.vialicensing.com) or through the respective patent owners individually for
22 the purpose of encoding or decoding bit streams in products that are compliant
23 with the ISO/IEC MPEG audio standards. Please note that most manufacturers of
24 Android devices already license these patent claims through Via Licensing or
25 directly from the patent owners, and therefore FDK AAC Codec software may
26 already be covered under those patent licenses when it is used for those
27 licensed purposes only.
28 
29 Commercially-licensed AAC software libraries, including floating-point versions
30 with enhanced sound quality, are also available from Fraunhofer. Users are
31 encouraged to check the Fraunhofer website for additional applications
32 information and documentation.
33 
34 2.    COPYRIGHT LICENSE
35 
36 Redistribution and use in source and binary forms, with or without modification,
37 are permitted without payment of copyright license fees provided that you
38 satisfy the following conditions:
39 
40 You must retain the complete text of this software license in redistributions of
41 the FDK AAC Codec or your modifications thereto in source code form.
42 
43 You must retain the complete text of this software license in the documentation
44 and/or other materials provided with redistributions of the FDK AAC Codec or
45 your modifications thereto in binary form. You must make available free of
46 charge copies of the complete source code of the FDK AAC Codec and your
47 modifications thereto to recipients of copies in binary form.
48 
49 The name of Fraunhofer may not be used to endorse or promote products derived
50 from this library without prior written permission.
51 
52 You may not charge copyright license fees for anyone to use, copy or distribute
53 the FDK AAC Codec software or your modifications thereto.
54 
55 Your modified versions of the FDK AAC Codec must carry prominent notices stating
56 that you changed the software and the date of any change. For modified versions
57 of the FDK AAC Codec, the term "Fraunhofer FDK AAC Codec Library for Android"
58 must be replaced by the term "Third-Party Modified Version of the Fraunhofer FDK
59 AAC Codec Library for Android."
60 
61 3.    NO PATENT LICENSE
62 
63 NO EXPRESS OR IMPLIED LICENSES TO ANY PATENT CLAIMS, including without
64 limitation the patents of Fraunhofer, ARE GRANTED BY THIS SOFTWARE LICENSE.
65 Fraunhofer provides no warranty of patent non-infringement with respect to this
66 software.
67 
68 You may use this FDK AAC Codec software or modifications thereto only for
69 purposes that are authorized by appropriate patent licenses.
70 
71 4.    DISCLAIMER
72 
73 This FDK AAC Codec software is provided by Fraunhofer on behalf of the copyright
74 holders and contributors "AS IS" and WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
75 including but not limited to the implied warranties of merchantability and
76 fitness for a particular purpose. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
77 CONTRIBUTORS BE LIABLE for any direct, indirect, incidental, special, exemplary,
78 or consequential damages, including but not limited to procurement of substitute
79 goods or services; loss of use, data, or profits, or business interruption,
80 however caused and on any theory of liability, whether in contract, strict
81 liability, or tort (including negligence), arising in any way out of the use of
82 this software, even if advised of the possibility of such damage.
83 
84 5.    CONTACT INFORMATION
85 
86 Fraunhofer Institute for Integrated Circuits IIS
87 Attention: Audio and Multimedia Departments - FDK AAC LL
88 Am Wolfsmantel 33
89 91058 Erlangen, Germany
90 
91 www.iis.fraunhofer.de/amm
92 amm-info@iis.fraunhofer.de
93 ----------------------------------------------------------------------------- */
94 
95 /**************************** 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 */
pcmLimiter_Create(unsigned int maxAttackMs,unsigned int releaseMs,FIXP_DBL threshold,unsigned int maxChannels,UINT maxSampleRate)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 */
pcmLimiter_Apply(TDLimiterPtr limiter,PCM_LIM * samplesIn,INT_PCM * samplesOut,FIXP_DBL * RESTRICT pGain,const INT * RESTRICT gain_scale,const UINT gain_size,const UINT gain_delay,const UINT nSamples)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 */
pcmLimiter_SetThreshold(TDLimiterPtr limiter,FIXP_DBL 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 */
pcmLimiter_Reset(TDLimiterPtr 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 */
pcmLimiter_Destroy(TDLimiterPtr 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 */
pcmLimiter_GetDelay(TDLimiterPtr limiter)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 */
pcmLimiter_GetMaxGainReduction(TDLimiterPtr limiter)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 */
pcmLimiter_SetNChannels(TDLimiterPtr limiter,unsigned int nChannels)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 */
pcmLimiter_SetSampleRate(TDLimiterPtr limiter,UINT sampleRate)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 */
pcmLimiter_SetAttack(TDLimiterPtr limiter,unsigned int attackMs)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 */
pcmLimiter_SetRelease(TDLimiterPtr limiter,unsigned int releaseMs)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. */
pcmLimiter_GetLibInfo(LIB_INFO * info)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