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