/src/aac/libPCMutils/src/limiter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* ----------------------------------------------------------------------------- |
2 | | Software License for The Fraunhofer FDK AAC Codec Library for Android |
3 | | |
4 | | © Copyright 1995 - 2020 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 | 0 | #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 | 0 | UINT maxSampleRate) { |
115 | 0 | TDLimiterPtr limiter = NULL; |
116 | 0 | unsigned int attack, release; |
117 | 0 | FIXP_DBL attackConst, releaseConst, exponent; |
118 | 0 | INT e_ans; |
119 | | |
120 | | /* calc attack and release time in samples */ |
121 | 0 | attack = (unsigned int)(maxAttackMs * maxSampleRate / 1000); |
122 | 0 | release = (unsigned int)(releaseMs * maxSampleRate / 1000); |
123 | | |
124 | | /* alloc limiter struct */ |
125 | 0 | limiter = (TDLimiterPtr)FDKcalloc(1, sizeof(struct TDLimiter)); |
126 | 0 | if (!limiter) return NULL; |
127 | | |
128 | | /* alloc max and delay buffers */ |
129 | 0 | limiter->maxBuf = (FIXP_DBL*)FDKcalloc(attack + 1, sizeof(FIXP_DBL)); |
130 | 0 | limiter->delayBuf = |
131 | 0 | (FIXP_DBL*)FDKcalloc(attack * maxChannels, sizeof(FIXP_DBL)); |
132 | |
|
133 | 0 | if (!limiter->maxBuf || !limiter->delayBuf) { |
134 | 0 | pcmLimiter_Destroy(limiter); |
135 | 0 | return NULL; |
136 | 0 | } |
137 | | |
138 | | /* attackConst = pow(0.1, 1.0 / (attack + 1)) */ |
139 | 0 | exponent = invFixp(attack + 1); |
140 | 0 | attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); |
141 | 0 | attackConst = scaleValue(attackConst, e_ans); |
142 | | |
143 | | /* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */ |
144 | 0 | exponent = invFixp(release + 1); |
145 | 0 | releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); |
146 | 0 | releaseConst = scaleValue(releaseConst, e_ans); |
147 | | |
148 | | /* init parameters */ |
149 | 0 | limiter->attackMs = maxAttackMs; |
150 | 0 | limiter->maxAttackMs = maxAttackMs; |
151 | 0 | limiter->releaseMs = releaseMs; |
152 | 0 | limiter->attack = attack; |
153 | 0 | limiter->attackConst = attackConst; |
154 | 0 | limiter->releaseConst = releaseConst; |
155 | 0 | limiter->threshold = threshold; |
156 | 0 | limiter->channels = maxChannels; |
157 | 0 | limiter->maxChannels = maxChannels; |
158 | 0 | limiter->sampleRate = maxSampleRate; |
159 | 0 | limiter->maxSampleRate = maxSampleRate; |
160 | |
|
161 | 0 | pcmLimiter_Reset(limiter); |
162 | |
|
163 | 0 | return limiter; |
164 | 0 | } |
165 | | |
166 | | /* apply limiter */ |
167 | | TDLIMITER_ERROR pcmLimiter_Apply(TDLimiterPtr limiter, PCM_LIM* samplesIn, |
168 | | INT_PCM* samplesOut, FIXP_DBL* pGainPerSample, |
169 | 0 | const INT scaling, const UINT nSamples) { |
170 | 0 | unsigned int i, j; |
171 | 0 | FIXP_DBL tmp2; |
172 | 0 | FIXP_DBL tmp, old, gain, additionalGain = 0; |
173 | 0 | FIXP_DBL minGain = FL2FXCONST_DBL(1.0f / (1 << 1)); |
174 | 0 | UINT additionalGainAvailable = 1; |
175 | |
|
176 | 0 | if (limiter == NULL) return TDLIMIT_INVALID_HANDLE; |
177 | | |
178 | 0 | { |
179 | 0 | unsigned int channels = limiter->channels; |
180 | 0 | unsigned int attack = limiter->attack; |
181 | 0 | FIXP_DBL attackConst = limiter->attackConst; |
182 | 0 | FIXP_DBL releaseConst = limiter->releaseConst; |
183 | 0 | FIXP_DBL threshold = limiter->threshold >> scaling; |
184 | |
|
185 | 0 | FIXP_DBL max = limiter->max; |
186 | 0 | FIXP_DBL* maxBuf = limiter->maxBuf; |
187 | 0 | unsigned int maxBufIdx = limiter->maxBufIdx; |
188 | 0 | FIXP_DBL cor = limiter->cor; |
189 | 0 | FIXP_DBL* delayBuf = limiter->delayBuf; |
190 | 0 | unsigned int delayBufIdx = limiter->delayBufIdx; |
191 | |
|
192 | 0 | FIXP_DBL smoothState0 = limiter->smoothState0; |
193 | |
|
194 | 0 | if (limiter->scaling != scaling) { |
195 | 0 | scaleValuesSaturate(delayBuf, attack * channels, |
196 | 0 | limiter->scaling - scaling); |
197 | 0 | scaleValuesSaturate(maxBuf, attack + 1, limiter->scaling - scaling); |
198 | 0 | max = scaleValueSaturate(max, limiter->scaling - scaling); |
199 | 0 | limiter->scaling = scaling; |
200 | 0 | } |
201 | |
|
202 | 0 | if (pGainPerSample == NULL) { |
203 | 0 | additionalGainAvailable = 0; |
204 | 0 | } |
205 | |
|
206 | 0 | for (i = 0; i < nSamples; i++) { |
207 | | /* get maximum absolute sample value of all channels, including the |
208 | | * additional gain. */ |
209 | 0 | tmp = (FIXP_DBL)0; |
210 | 0 | for (j = 0; j < channels; j++) { |
211 | 0 | tmp2 = PCM_LIM2FIXP_DBL(samplesIn[j]); |
212 | 0 | tmp2 = |
213 | 0 | (tmp2 == (FIXP_DBL)MINVAL_DBL) ? (FIXP_DBL)MAXVAL_DBL : fAbs(tmp2); |
214 | 0 | tmp = fMax(tmp, tmp2); |
215 | 0 | } |
216 | |
|
217 | 0 | if (additionalGainAvailable) { |
218 | 0 | additionalGain = pGainPerSample[i]; |
219 | 0 | tmp = fMult(tmp, additionalGain); |
220 | 0 | } |
221 | | |
222 | | /* set threshold as lower border to save calculations in running maximum |
223 | | * algorithm */ |
224 | 0 | tmp = fMax(tmp, threshold); |
225 | | |
226 | | /* running maximum */ |
227 | 0 | old = maxBuf[maxBufIdx]; |
228 | 0 | maxBuf[maxBufIdx] = tmp; |
229 | |
|
230 | 0 | if (tmp >= max) { |
231 | | /* new sample is greater than old maximum, so it is the new maximum */ |
232 | 0 | max = tmp; |
233 | 0 | } else if (old < max) { |
234 | | /* maximum does not change, as the sample, which has left the window was |
235 | | not the maximum */ |
236 | 0 | } else { |
237 | | /* the old maximum has left the window, we have to search the complete |
238 | | buffer for the new max */ |
239 | 0 | max = maxBuf[0]; |
240 | 0 | for (j = 1; j <= attack; j++) { |
241 | 0 | max = fMax(max, maxBuf[j]); |
242 | 0 | } |
243 | 0 | } |
244 | 0 | maxBufIdx++; |
245 | 0 | if (maxBufIdx >= attack + 1) maxBufIdx = 0; |
246 | | |
247 | | /* calc gain */ |
248 | | /* gain is downscaled by one, so that gain = 1.0 can be represented */ |
249 | 0 | if (max > threshold) { |
250 | 0 | gain = fDivNorm(threshold, max) >> 1; |
251 | 0 | } else { |
252 | 0 | gain = FL2FXCONST_DBL(1.0f / (1 << 1)); |
253 | 0 | } |
254 | | |
255 | | /* gain smoothing, method: TDL_EXPONENTIAL */ |
256 | | /* first order IIR filter with attack correction to avoid overshoots */ |
257 | | |
258 | | /* correct the 'aiming' value of the exponential attack to avoid the |
259 | | * remaining overshoot */ |
260 | 0 | if (gain < smoothState0) { |
261 | 0 | cor = fMin(cor, |
262 | 0 | fMultDiv2((gain - fMultDiv2(FL2FXCONST_SGL(0.1f * (1 << 1)), |
263 | 0 | smoothState0)), |
264 | 0 | FL2FXCONST_SGL(1.11111111f / (1 << 1))) |
265 | 0 | << 2); |
266 | 0 | } else { |
267 | 0 | cor = gain; |
268 | 0 | } |
269 | | |
270 | | /* smoothing filter */ |
271 | 0 | if (cor < smoothState0) { |
272 | 0 | smoothState0 = |
273 | 0 | fMult(attackConst, (smoothState0 - cor)) + cor; /* attack */ |
274 | 0 | smoothState0 = fMax(smoothState0, gain); /* avoid overshooting target */ |
275 | 0 | } else { |
276 | | /* sign inversion twice to round towards +infinity, |
277 | | so that gain can converge to 1.0 again, |
278 | | for bit-identical output when limiter is not active */ |
279 | 0 | smoothState0 = |
280 | 0 | -fMult(releaseConst, -(smoothState0 - cor)) + cor; /* release */ |
281 | 0 | } |
282 | |
|
283 | 0 | gain = smoothState0; |
284 | |
|
285 | 0 | FIXP_DBL* p_delayBuf = &delayBuf[delayBufIdx * channels + 0]; |
286 | 0 | if (gain < FL2FXCONST_DBL(1.0f / (1 << 1))) { |
287 | 0 | gain <<= 1; |
288 | | /* lookahead delay, apply gain */ |
289 | 0 | for (j = 0; j < channels; j++) { |
290 | 0 | tmp = p_delayBuf[j]; |
291 | |
|
292 | 0 | if (additionalGainAvailable) { |
293 | 0 | p_delayBuf[j] = fMult((FIXP_PCM_LIM)samplesIn[j], additionalGain); |
294 | 0 | } else { |
295 | 0 | p_delayBuf[j] = PCM_LIM2FIXP_DBL(samplesIn[j]); |
296 | 0 | } |
297 | | |
298 | | /* Apply gain to delayed signal */ |
299 | 0 | tmp = fMultDiv2(tmp, gain); |
300 | | #if (SAMPLE_BITS == DFRACT_BITS) |
301 | | samplesOut[j] = (INT_PCM)FX_DBL2FX_PCM( |
302 | | (FIXP_DBL)SATURATE_LEFT_SHIFT(tmp, scaling + 1, DFRACT_BITS)); |
303 | | #else |
304 | 0 | samplesOut[j] = (INT_PCM)FX_DBL2FX_PCM((FIXP_DBL)SATURATE_LEFT_SHIFT( |
305 | 0 | tmp + ((FIXP_DBL)0x8000 >> (scaling + 1)), scaling + 1, |
306 | 0 | DFRACT_BITS)); |
307 | 0 | #endif |
308 | 0 | } |
309 | 0 | gain >>= 1; |
310 | 0 | } else { |
311 | | /* lookahead delay, apply gain=1.0f */ |
312 | 0 | for (j = 0; j < channels; j++) { |
313 | 0 | tmp = p_delayBuf[j]; |
314 | 0 | if (additionalGainAvailable) { |
315 | 0 | p_delayBuf[j] = fMult((FIXP_PCM_LIM)samplesIn[j], additionalGain); |
316 | 0 | } else { |
317 | 0 | p_delayBuf[j] = PCM_LIM2FIXP_DBL(samplesIn[j]); |
318 | 0 | } |
319 | |
|
320 | | #if (SAMPLE_BITS == DFRACT_BITS) |
321 | | samplesOut[j] = (INT_PCM)FX_DBL2FX_PCM( |
322 | | (FIXP_DBL)SATURATE_LEFT_SHIFT(tmp, scaling, DFRACT_BITS)); |
323 | | #else |
324 | 0 | samplesOut[j] = (INT_PCM)FX_DBL2FX_PCM((FIXP_DBL)SATURATE_LEFT_SHIFT( |
325 | 0 | (tmp >> 1) + ((FIXP_DBL)0x8000 >> (scaling + 1)), scaling + 1, |
326 | 0 | DFRACT_BITS)); |
327 | 0 | #endif |
328 | 0 | } |
329 | 0 | } |
330 | |
|
331 | 0 | delayBufIdx++; |
332 | 0 | if (delayBufIdx >= attack) { |
333 | 0 | delayBufIdx = 0; |
334 | 0 | } |
335 | | |
336 | | /* save minimum gain factor */ |
337 | 0 | if (gain < minGain) { |
338 | 0 | minGain = gain; |
339 | 0 | } |
340 | | |
341 | | /* advance sample pointer by <channel> samples */ |
342 | 0 | samplesIn += channels; |
343 | 0 | samplesOut += channels; |
344 | 0 | } |
345 | |
|
346 | 0 | limiter->max = max; |
347 | 0 | limiter->maxBufIdx = maxBufIdx; |
348 | 0 | limiter->cor = cor; |
349 | 0 | limiter->delayBufIdx = delayBufIdx; |
350 | |
|
351 | 0 | limiter->smoothState0 = smoothState0; |
352 | |
|
353 | 0 | limiter->minGain = minGain; |
354 | |
|
355 | 0 | return TDLIMIT_OK; |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | | /* set limiter threshold */ |
360 | | TDLIMITER_ERROR pcmLimiter_SetThreshold(TDLimiterPtr limiter, |
361 | 0 | FIXP_DBL threshold) { |
362 | 0 | if (limiter == NULL) return TDLIMIT_INVALID_HANDLE; |
363 | | |
364 | 0 | limiter->threshold = threshold; |
365 | |
|
366 | 0 | return TDLIMIT_OK; |
367 | 0 | } |
368 | | |
369 | | /* reset limiter */ |
370 | 0 | TDLIMITER_ERROR pcmLimiter_Reset(TDLimiterPtr limiter) { |
371 | 0 | if (limiter != NULL) { |
372 | 0 | limiter->maxBufIdx = 0; |
373 | 0 | limiter->delayBufIdx = 0; |
374 | 0 | limiter->max = (FIXP_DBL)0; |
375 | 0 | limiter->cor = FL2FXCONST_DBL(1.0f / (1 << 1)); |
376 | 0 | limiter->smoothState0 = FL2FXCONST_DBL(1.0f / (1 << 1)); |
377 | 0 | limiter->minGain = FL2FXCONST_DBL(1.0f / (1 << 1)); |
378 | 0 | limiter->scaling = 0; |
379 | |
|
380 | 0 | FDKmemset(limiter->maxBuf, 0, (limiter->attack + 1) * sizeof(FIXP_DBL)); |
381 | 0 | FDKmemset(limiter->delayBuf, 0, |
382 | 0 | limiter->attack * limiter->channels * sizeof(FIXP_DBL)); |
383 | 0 | } else { |
384 | 0 | return TDLIMIT_INVALID_HANDLE; |
385 | 0 | } |
386 | | |
387 | 0 | return TDLIMIT_OK; |
388 | 0 | } |
389 | | |
390 | | /* destroy limiter */ |
391 | 0 | TDLIMITER_ERROR pcmLimiter_Destroy(TDLimiterPtr limiter) { |
392 | 0 | if (limiter != NULL) { |
393 | 0 | FDKfree(limiter->maxBuf); |
394 | 0 | FDKfree(limiter->delayBuf); |
395 | |
|
396 | 0 | FDKfree(limiter); |
397 | 0 | } else { |
398 | 0 | return TDLIMIT_INVALID_HANDLE; |
399 | 0 | } |
400 | 0 | return TDLIMIT_OK; |
401 | 0 | } |
402 | | |
403 | | /* get delay in samples */ |
404 | 0 | unsigned int pcmLimiter_GetDelay(TDLimiterPtr limiter) { |
405 | 0 | FDK_ASSERT(limiter != NULL); |
406 | 0 | return limiter->attack; |
407 | 0 | } |
408 | | |
409 | | /* get maximum gain reduction of last processed block */ |
410 | 0 | INT pcmLimiter_GetMaxGainReduction(TDLimiterPtr limiter) { |
411 | | /* maximum gain reduction in dB = -20 * log10(limiter->minGain) |
412 | | = -20 * log2(limiter->minGain)/log2(10) = -6.0206*log2(limiter->minGain) */ |
413 | 0 | int e_ans; |
414 | 0 | FIXP_DBL loggain, maxGainReduction; |
415 | |
|
416 | 0 | FDK_ASSERT(limiter != NULL); |
417 | | |
418 | 0 | loggain = fLog2(limiter->minGain, 1, &e_ans); |
419 | |
|
420 | 0 | maxGainReduction = fMult(loggain, FL2FXCONST_DBL(-6.0206f / (1 << 3))); |
421 | |
|
422 | 0 | return fixp_roundToInt(maxGainReduction, (e_ans + 3)); |
423 | 0 | } |
424 | | |
425 | | /* set number of channels */ |
426 | | TDLIMITER_ERROR pcmLimiter_SetNChannels(TDLimiterPtr limiter, |
427 | 0 | unsigned int nChannels) { |
428 | 0 | if (limiter == NULL) return TDLIMIT_INVALID_HANDLE; |
429 | | |
430 | 0 | if (nChannels > limiter->maxChannels) return TDLIMIT_INVALID_PARAMETER; |
431 | | |
432 | 0 | limiter->channels = nChannels; |
433 | | // pcmLimiter_Reset(limiter); |
434 | |
|
435 | 0 | return TDLIMIT_OK; |
436 | 0 | } |
437 | | |
438 | | /* set sampling rate */ |
439 | | TDLIMITER_ERROR pcmLimiter_SetSampleRate(TDLimiterPtr limiter, |
440 | 0 | UINT sampleRate) { |
441 | 0 | unsigned int attack, release; |
442 | 0 | FIXP_DBL attackConst, releaseConst, exponent; |
443 | 0 | INT e_ans; |
444 | |
|
445 | 0 | if (limiter == NULL) return TDLIMIT_INVALID_HANDLE; |
446 | | |
447 | 0 | if (sampleRate > limiter->maxSampleRate) return TDLIMIT_INVALID_PARAMETER; |
448 | | |
449 | | /* update attack and release time in samples */ |
450 | 0 | attack = (unsigned int)(limiter->attackMs * sampleRate / 1000); |
451 | 0 | release = (unsigned int)(limiter->releaseMs * sampleRate / 1000); |
452 | | |
453 | | /* attackConst = pow(0.1, 1.0 / (attack + 1)) */ |
454 | 0 | exponent = invFixp(attack + 1); |
455 | 0 | attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); |
456 | 0 | attackConst = scaleValue(attackConst, e_ans); |
457 | | |
458 | | /* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */ |
459 | 0 | exponent = invFixp(release + 1); |
460 | 0 | releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); |
461 | 0 | releaseConst = scaleValue(releaseConst, e_ans); |
462 | |
|
463 | 0 | limiter->attack = attack; |
464 | 0 | limiter->attackConst = attackConst; |
465 | 0 | limiter->releaseConst = releaseConst; |
466 | 0 | limiter->sampleRate = sampleRate; |
467 | | |
468 | | /* reset */ |
469 | | // pcmLimiter_Reset(limiter); |
470 | |
|
471 | 0 | return TDLIMIT_OK; |
472 | 0 | } |
473 | | |
474 | | /* set attack time */ |
475 | | TDLIMITER_ERROR pcmLimiter_SetAttack(TDLimiterPtr limiter, |
476 | 0 | unsigned int attackMs) { |
477 | 0 | unsigned int attack; |
478 | 0 | FIXP_DBL attackConst, exponent; |
479 | 0 | INT e_ans; |
480 | |
|
481 | 0 | if (limiter == NULL) return TDLIMIT_INVALID_HANDLE; |
482 | | |
483 | 0 | if (attackMs > limiter->maxAttackMs) return TDLIMIT_INVALID_PARAMETER; |
484 | | |
485 | | /* calculate attack time in samples */ |
486 | 0 | attack = (unsigned int)(attackMs * limiter->sampleRate / 1000); |
487 | | |
488 | | /* attackConst = pow(0.1, 1.0 / (attack + 1)) */ |
489 | 0 | exponent = invFixp(attack + 1); |
490 | 0 | attackConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); |
491 | 0 | attackConst = scaleValue(attackConst, e_ans); |
492 | |
|
493 | 0 | limiter->attack = attack; |
494 | 0 | limiter->attackConst = attackConst; |
495 | 0 | limiter->attackMs = attackMs; |
496 | |
|
497 | 0 | return TDLIMIT_OK; |
498 | 0 | } |
499 | | |
500 | | /* set release time */ |
501 | | TDLIMITER_ERROR pcmLimiter_SetRelease(TDLimiterPtr limiter, |
502 | 0 | unsigned int releaseMs) { |
503 | 0 | unsigned int release; |
504 | 0 | FIXP_DBL releaseConst, exponent; |
505 | 0 | INT e_ans; |
506 | |
|
507 | 0 | if (limiter == NULL) return TDLIMIT_INVALID_HANDLE; |
508 | | |
509 | | /* calculate release time in samples */ |
510 | 0 | release = (unsigned int)(releaseMs * limiter->sampleRate / 1000); |
511 | | |
512 | | /* releaseConst = (float)pow(0.1, 1.0 / (release + 1)) */ |
513 | 0 | exponent = invFixp(release + 1); |
514 | 0 | releaseConst = fPow(FL2FXCONST_DBL(0.1f), 0, exponent, 0, &e_ans); |
515 | 0 | releaseConst = scaleValue(releaseConst, e_ans); |
516 | |
|
517 | 0 | limiter->releaseConst = releaseConst; |
518 | 0 | limiter->releaseMs = releaseMs; |
519 | |
|
520 | 0 | return TDLIMIT_OK; |
521 | 0 | } |
522 | | |
523 | | /* Get library info for this module. */ |
524 | 0 | TDLIMITER_ERROR pcmLimiter_GetLibInfo(LIB_INFO* info) { |
525 | 0 | int i; |
526 | |
|
527 | 0 | if (info == NULL) { |
528 | 0 | return TDLIMIT_INVALID_PARAMETER; |
529 | 0 | } |
530 | | |
531 | | /* Search for next free tab */ |
532 | 0 | for (i = 0; i < FDK_MODULE_LAST; i++) { |
533 | 0 | if (info[i].module_id == FDK_NONE) break; |
534 | 0 | } |
535 | 0 | if (i == FDK_MODULE_LAST) { |
536 | 0 | return TDLIMIT_UNKNOWN; |
537 | 0 | } |
538 | | |
539 | | /* Add the library info */ |
540 | 0 | info[i].module_id = FDK_TDLIMIT; |
541 | 0 | info[i].version = |
542 | 0 | LIB_VERSION(PCMUTIL_LIB_VL0, PCMUTIL_LIB_VL1, PCMUTIL_LIB_VL2); |
543 | 0 | LIB_VERSION_STRING(info + i); |
544 | 0 | info[i].build_date = PCMUTIL_LIB_BUILD_DATE; |
545 | 0 | info[i].build_time = PCMUTIL_LIB_BUILD_TIME; |
546 | 0 | info[i].title = TDLIMIT_LIB_TITLE; |
547 | | |
548 | | /* Set flags */ |
549 | 0 | info[i].flags = CAPF_LIMITER; |
550 | | |
551 | | /* Add lib info for FDK tools (if not yet done). */ |
552 | 0 | FDK_toolsGetLibInfo(info); |
553 | |
|
554 | 0 | return TDLIMIT_OK; |
555 | 0 | } |