• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004-2010 NXP Software
3  * Copyright (C) 2010 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 /****************************************************************************************/
19 /*                                                                                      */
20 /*  Includes                                                                            */
21 /*                                                                                      */
22 /****************************************************************************************/
23 
24 #include <system/audio.h>
25 #include "LVREV_Private.h"
26 #include "Filter.h"
27 
28 /****************************************************************************************/
29 /*                                                                                      */
30 /* FUNCTION:                LVREV_ApplyNewSettings                                      */
31 /*                                                                                      */
32 /* DESCRIPTION:                                                                         */
33 /*  Applies the new control parameters                                                  */
34 /*                                                                                      */
35 /* PARAMETERS:                                                                          */
36 /*  pPrivate                Pointer to the instance private parameters                  */
37 /*                                                                                      */
38 /* RETURNS:                                                                             */
39 /*  LVREV_Success           Succeeded                                                   */
40 /*  LVREV_NULLADDRESS       When pPrivate is NULL                                       */
41 /*                                                                                      */
42 /* NOTES:                                                                               */
43 /*                                                                                      */
44 /****************************************************************************************/
45 
LVREV_ApplyNewSettings(LVREV_Instance_st * pPrivate)46 LVREV_ReturnStatus_en LVREV_ApplyNewSettings(LVREV_Instance_st* pPrivate) {
47     LVM_Mode_en OperatingMode;
48     LVM_INT32 NumberOfDelayLines;
49 
50     /* Check for NULL pointer */
51     if (pPrivate == LVM_NULL) {
52         return LVREV_NULLADDRESS;
53     }
54 
55     OperatingMode = pPrivate->NewParams.OperatingMode;
56 
57     if (pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_4) {
58         NumberOfDelayLines = 4;
59     } else if (pPrivate->InstanceParams.NumDelays == LVREV_DELAYLINES_2) {
60         NumberOfDelayLines = 2;
61     } else {
62         NumberOfDelayLines = 1;
63     }
64 
65     /*
66      * Update the high pass filter coefficients
67      */
68     if ((pPrivate->NewParams.HPF != pPrivate->CurrentParams.HPF) ||
69         (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
70         (pPrivate->bFirstControl == LVM_TRUE)) {
71         LVM_FLOAT Omega;
72         FO_FLOAT_Coefs_t Coeffs;
73 
74         Omega = LVM_GetOmega(pPrivate->NewParams.HPF, pPrivate->NewParams.SampleRate);
75         LVM_FO_HPF(Omega, &Coeffs);
76         const std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
77                 Coeffs.A0, Coeffs.A1, 0.0, -(Coeffs.B1), 0.0};
78         pPrivate->pRevHPFBiquad.reset(
79                 new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1, coefs));
80     }
81 
82     /*
83      * Update the low pass filter coefficients
84      */
85     if ((pPrivate->NewParams.LPF != pPrivate->CurrentParams.LPF) ||
86         (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
87         (pPrivate->bFirstControl == LVM_TRUE)) {
88         LVM_FLOAT Omega;
89         FO_FLOAT_Coefs_t Coeffs;
90 
91         Coeffs.A0 = 1;
92         Coeffs.A1 = 0;
93         Coeffs.B1 = 0;
94         if (pPrivate->NewParams.LPF <= (LVM_FsTable[pPrivate->NewParams.SampleRate] >> 1)) {
95             Omega = LVM_GetOmega(pPrivate->NewParams.LPF, pPrivate->NewParams.SampleRate);
96 
97             /*
98              * Do not apply filter if w =2*pi*fc/fs >= 2.9
99              */
100             if (Omega <= (LVM_FLOAT)LVREV_2_9_INQ29) {
101                 LVM_FO_LPF(Omega, &Coeffs);
102             }
103         }
104         const std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
105                 Coeffs.A0, Coeffs.A1, 0.0, -(Coeffs.B1), 0.0};
106         pPrivate->pRevLPFBiquad.reset(
107                 new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1, coefs));
108     }
109 
110     /*
111      * Calculate the room size parameter
112      */
113     if (pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) {
114         /* Room size range is 10ms to 200ms
115          * 0%   -- 10ms
116          * 50%  -- 65ms
117          * 100% -- 120ms
118          */
119         pPrivate->RoomSizeInms = 10 + (((pPrivate->NewParams.RoomSize * 11) + 5) / 10);
120     }
121 
122     /*
123      * Update the T delay number of samples and the all pass delay number of samples
124      */
125     if ((pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) ||
126         (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
127         (pPrivate->bFirstControl == LVM_TRUE)) {
128         LVM_UINT32 Temp;
129         LVM_INT32 APDelaySize;
130         LVM_INT32 Fs = LVM_GetFsFromTable(pPrivate->NewParams.SampleRate);
131         LVM_UINT32 DelayLengthSamples = (LVM_UINT32)(Fs * pPrivate->RoomSizeInms);
132         LVM_INT16 i;
133         LVM_FLOAT ScaleTable[] = {LVREV_T_3_Power_minus0_on_4, LVREV_T_3_Power_minus1_on_4,
134                                   LVREV_T_3_Power_minus2_on_4, LVREV_T_3_Power_minus3_on_4};
135 
136         /*
137          * For each delay line
138          */
139         for (i = 0; i < NumberOfDelayLines; i++) {
140             if (i != 0) {
141                 LVM_FLOAT Temp1; /* to avoid QAC warning on type conversion */
142 
143                 Temp1 = (LVM_FLOAT)DelayLengthSamples;
144                 Temp = (LVM_UINT32)(Temp1 * ScaleTable[i]);
145             } else {
146                 Temp = DelayLengthSamples;
147             }
148             APDelaySize = Temp / 1500;
149 
150             /*
151              * Set the fixed delay
152              */
153 
154             Temp = (LVREV_MAX_T_DELAY[i] - LVREV_MAX_AP_DELAY[i]) * Fs / 192000;
155             pPrivate->Delay_AP[i] = pPrivate->T[i] - Temp;
156 
157             /*
158              * Set the tap selection
159              */
160             if (pPrivate->AB_Selection) {
161                 /* Smooth from tap A to tap B */
162                 pPrivate->pOffsetB[i] = &pPrivate->pDelay_T[i][pPrivate->T[i] - Temp - APDelaySize];
163                 pPrivate->B_DelaySize[i] = APDelaySize;
164                 pPrivate->Mixer_APTaps[i].Target1 = 0;
165                 pPrivate->Mixer_APTaps[i].Target2 = 1.0f;
166             } else {
167                 /* Smooth from tap B to tap A */
168                 pPrivate->pOffsetA[i] = &pPrivate->pDelay_T[i][pPrivate->T[i] - Temp - APDelaySize];
169                 pPrivate->A_DelaySize[i] = APDelaySize;
170                 pPrivate->Mixer_APTaps[i].Target2 = 0;
171                 pPrivate->Mixer_APTaps[i].Target1 = 1.0f;
172             }
173 
174             /*
175              * Set the maximum block size to the smallest delay size
176              */
177             pPrivate->MaxBlkLen = Temp;
178             if (pPrivate->MaxBlkLen > pPrivate->A_DelaySize[i]) {
179                 pPrivate->MaxBlkLen = pPrivate->A_DelaySize[i];
180             }
181             if (pPrivate->MaxBlkLen > pPrivate->B_DelaySize[i]) {
182                 pPrivate->MaxBlkLen = pPrivate->B_DelaySize[i];
183             }
184         }
185         if (pPrivate->AB_Selection) {
186             pPrivate->AB_Selection = 0;
187         } else {
188             pPrivate->AB_Selection = 1;
189         }
190 
191         /*
192          * Limit the maximum block length
193          */
194         /* Just as a precausion, but no problem if we remove this line      */
195         pPrivate->MaxBlkLen = pPrivate->MaxBlkLen - 2;
196         if (pPrivate->MaxBlkLen > pPrivate->InstanceParams.MaxBlockSize) {
197             pPrivate->MaxBlkLen = (LVM_INT32)pPrivate->InstanceParams.MaxBlockSize;
198         }
199     }
200 
201     /*
202      * Update the low pass filter coefficient
203      */
204     if ((pPrivate->NewParams.Damping != pPrivate->CurrentParams.Damping) ||
205         (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
206         (pPrivate->bFirstControl == LVM_TRUE)) {
207         LVM_INT32 Temp;
208         LVM_FLOAT Omega;
209         FO_FLOAT_Coefs_t Coeffs;
210         LVM_INT16 i;
211         LVM_INT16 Damping = (LVM_INT16)((pPrivate->NewParams.Damping * 100) + 1000);
212         LVM_FLOAT ScaleTable[] = {LVREV_T_3_Power_0_on_4, LVREV_T_3_Power_1_on_4,
213                                   LVREV_T_3_Power_2_on_4, LVREV_T_3_Power_3_on_4};
214 
215         /*
216          * For each filter
217          */
218         for (i = 0; i < NumberOfDelayLines; i++) {
219             if (i != 0) {
220                 Temp = (LVM_INT32)(ScaleTable[i] * Damping);
221             } else {
222                 Temp = Damping;
223             }
224             if (Temp <= (LVM_INT32)(LVM_FsTable[pPrivate->NewParams.SampleRate] >> 1)) {
225                 Omega = LVM_GetOmega(Temp, pPrivate->NewParams.SampleRate);
226                 LVM_FO_LPF(Omega, &Coeffs);
227             } else {
228                 Coeffs.A0 = 1;
229                 Coeffs.A1 = 0;
230                 Coeffs.B1 = 0;
231             }
232             const std::array<LVM_FLOAT, android::audio_utils::kBiquadNumCoefs> coefs = {
233                     Coeffs.A0, Coeffs.A1, 0.0, -(Coeffs.B1), 0.0};
234             pPrivate->revLPFBiquad[i].reset(
235                     new android::audio_utils::BiquadFilter<LVM_FLOAT>(FCC_1, coefs));
236         }
237     }
238 
239     /*
240      * Update All-pass filter mixer time constants
241      */
242     if ((pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) ||
243         (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
244         (pPrivate->NewParams.Density != pPrivate->CurrentParams.Density)) {
245         LVM_INT16 i;
246         LVM_FLOAT Alpha;
247         LVM_FLOAT AlphaTap;
248 
249         Alpha = LVM_Mixer_TimeConstant(LVREV_ALLPASS_TC,
250                                        LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), 1);
251 
252         AlphaTap = LVM_Mixer_TimeConstant(LVREV_ALLPASS_TAP_TC,
253                                           LVM_GetFsFromTable(pPrivate->NewParams.SampleRate), 1);
254 
255         for (i = 0; i < 4; i++) {
256             pPrivate->Mixer_APTaps[i].Alpha1 = AlphaTap;
257             pPrivate->Mixer_APTaps[i].Alpha2 = AlphaTap;
258             pPrivate->Mixer_SGFeedback[i].Alpha = Alpha;
259             pPrivate->Mixer_SGFeedforward[i].Alpha = Alpha;
260         }
261     }
262 
263     /*
264      * Update the feed back gain
265      */
266     if ((pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) ||
267         (pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
268         (pPrivate->NewParams.T60 != pPrivate->CurrentParams.T60) ||
269         (pPrivate->bFirstControl == LVM_TRUE)) {
270         LVM_FLOAT G[4]; /* Feedback gain (Q7.24) */
271 
272         if (pPrivate->NewParams.T60 == 0) {
273             G[3] = 0;
274             G[2] = 0;
275             G[1] = 0;
276             G[0] = 0;
277         } else {
278             LVM_FLOAT Temp1;
279             LVM_FLOAT Temp2;
280             LVM_INT16 i;
281             LVM_FLOAT ScaleTable[] = {LVREV_T_3_Power_minus0_on_4, LVREV_T_3_Power_minus1_on_4,
282                                       LVREV_T_3_Power_minus2_on_4, LVREV_T_3_Power_minus3_on_4};
283 
284             /*
285              * For each delay line
286              */
287             for (i = 0; i < NumberOfDelayLines; i++) {
288                 Temp1 = (3 * pPrivate->RoomSizeInms * ScaleTable[i]) / pPrivate->NewParams.T60;
289                 if (Temp1 >= (4)) {
290                     G[i] = 0;
291                 } else if ((Temp1 >= (2))) {
292                     Temp2 = LVM_Power10(-(Temp1 / 2.0f));
293                     Temp1 = LVM_Power10(-(Temp1 / 2.0f));
294                     Temp1 = Temp1 * Temp2;
295                 } else {
296                     Temp1 = LVM_Power10(-(Temp1));
297                 }
298                 if (NumberOfDelayLines == 1) {
299                     G[i] = Temp1;
300                 } else {
301                     LVM_FLOAT TempG;
302                     TempG = Temp1 * ONE_OVER_SQRT_TWO;
303                     G[i] = TempG;
304                 }
305             }
306         }
307 
308         /* Set up the feedback mixers for four delay lines */
309         pPrivate->FeedbackMixer[0].Target = G[0];
310         pPrivate->FeedbackMixer[1].Target = G[1];
311         pPrivate->FeedbackMixer[2].Target = G[2];
312         pPrivate->FeedbackMixer[3].Target = G[3];
313     }
314 
315     /*
316      * Calculate the gain correction
317      */
318     if ((pPrivate->NewParams.RoomSize != pPrivate->CurrentParams.RoomSize) ||
319         (pPrivate->NewParams.Level != pPrivate->CurrentParams.Level) ||
320         (pPrivate->NewParams.T60 != pPrivate->CurrentParams.T60)) {
321         LVM_INT32 Index = 0;
322         LVM_FLOAT Index_FLOAT;
323         LVM_INT32 i = 0;
324         LVM_FLOAT Gain = 0;
325         LVM_INT32 RoomSize = 0;
326         LVM_FLOAT T60;
327         LVM_FLOAT Coefs[5];
328 
329         if (pPrivate->NewParams.RoomSize == 0) {
330             RoomSize = 1;
331         } else {
332             RoomSize = (LVM_INT32)pPrivate->NewParams.RoomSize;
333         }
334 
335         if (pPrivate->NewParams.T60 < 100) {
336             T60 = 100 * LVREV_T60_SCALE;
337         } else {
338             T60 = pPrivate->NewParams.T60 * LVREV_T60_SCALE;
339         }
340 
341         /* Find the nearest room size in table */
342         for (i = 0; i < 24; i++) {
343             if (RoomSize <= LVREV_GainPolyTable[i][0]) {
344                 Index = i;
345                 break;
346             }
347         }
348 
349         if (RoomSize == LVREV_GainPolyTable[Index][0]) {
350             /* Take table values if the room size is in table */
351             for (i = 1; i < 5; i++) {
352                 Coefs[i - 1] = LVREV_GainPolyTable[Index][i];
353             }
354             Coefs[4] = 0;
355             Gain = LVM_Polynomial(3, Coefs, T60); /* Q.24 result */
356         } else {
357             /* Interpolate the gain between nearest room sizes */
358 
359             LVM_FLOAT Gain1, Gain2;
360             LVM_INT32 Tot_Dist, Dist;
361 
362             Tot_Dist = (LVM_UINT32)LVREV_GainPolyTable[Index][0] -
363                        (LVM_UINT32)LVREV_GainPolyTable[Index - 1][0];
364             Dist = RoomSize - (LVM_UINT32)LVREV_GainPolyTable[Index - 1][0];
365 
366             /* Get gain for first */
367             for (i = 1; i < 5; i++) {
368                 Coefs[i - 1] = LVREV_GainPolyTable[Index - 1][i];
369             }
370             Coefs[4] = 0;
371 
372             Gain1 = LVM_Polynomial(3, Coefs, T60); /* Q.24 result */
373 
374             /* Get gain for second */
375             for (i = 1; i < 5; i++) {
376                 Coefs[i - 1] = LVREV_GainPolyTable[Index][i];
377             }
378             Coefs[4] = 0;
379 
380             Gain2 = LVM_Polynomial(3, Coefs, T60); /* Q.24 result */
381 
382             /* Linear Interpolate the gain */
383             Gain = Gain1 + (((Gain2 - Gain1) * Dist) / (Tot_Dist));
384         }
385 
386         /*
387          * Get the inverse of gain: Q.15
388          * Gain is mostly above one except few cases, take only gains above 1
389          */
390         if (Gain < 1) {
391             pPrivate->Gain = 1;
392         } else {
393             pPrivate->Gain = 1 / Gain;
394         }
395 
396         Index_FLOAT = 100.0f / (LVM_FLOAT)(100 + pPrivate->NewParams.Level);
397         pPrivate->Gain = pPrivate->Gain * Index_FLOAT;
398         pPrivate->GainMixer.Target = (pPrivate->Gain * Index_FLOAT) / 2;
399     }
400 
401     /*
402      * Update the all pass comb filter coefficient
403      */
404     if ((pPrivate->NewParams.Density != pPrivate->CurrentParams.Density) ||
405         (pPrivate->bFirstControl == LVM_TRUE)) {
406         LVM_INT16 i;
407         LVM_FLOAT b = (LVM_FLOAT)pPrivate->NewParams.Density * LVREV_B_8_on_1000;
408 
409         for (i = 0; i < 4; i++) {
410             pPrivate->Mixer_SGFeedback[i].Target = b;
411             pPrivate->Mixer_SGFeedforward[i].Target = b;
412         }
413     }
414 
415     /*
416      * Update the bypass mixer time constant
417      */
418     if ((pPrivate->NewParams.SampleRate != pPrivate->CurrentParams.SampleRate) ||
419         (pPrivate->bFirstControl == LVM_TRUE)) {
420         LVM_UINT16 NumChannels = 1; /* Assume MONO format */
421         LVM_FLOAT Alpha;
422 
423         Alpha = LVM_Mixer_TimeConstant(LVREV_FEEDBACKMIXER_TC,
424                                        LVM_GetFsFromTable(pPrivate->NewParams.SampleRate),
425                                        NumChannels);
426         pPrivate->FeedbackMixer[0].Alpha = Alpha;
427         pPrivate->FeedbackMixer[1].Alpha = Alpha;
428         pPrivate->FeedbackMixer[2].Alpha = Alpha;
429         pPrivate->FeedbackMixer[3].Alpha = Alpha;
430 
431         NumChannels = 2; /* Always stereo output */
432         pPrivate->BypassMixer.Alpha1 = LVM_Mixer_TimeConstant(
433                 LVREV_BYPASSMIXER_TC, LVM_GetFsFromTable(pPrivate->NewParams.SampleRate),
434                 NumChannels);
435         pPrivate->BypassMixer.Alpha2 = pPrivate->BypassMixer.Alpha1;
436         pPrivate->GainMixer.Alpha = pPrivate->BypassMixer.Alpha1;
437     }
438 
439     /*
440      * Update the bypass mixer targets
441      */
442     if ((pPrivate->NewParams.Level != pPrivate->CurrentParams.Level) &&
443         (pPrivate->NewParams.OperatingMode == LVM_MODE_ON)) {
444         pPrivate->BypassMixer.Target2 = (LVM_FLOAT)(pPrivate->NewParams.Level) / 100.0f;
445         pPrivate->BypassMixer.Target1 = 0x00000000;
446         if ((pPrivate->NewParams.Level == 0) && (pPrivate->bFirstControl == LVM_FALSE)) {
447             pPrivate->BypassMixer.CallbackSet2 = LVM_TRUE;
448         }
449         if (pPrivate->NewParams.Level != 0) {
450             pPrivate->bDisableReverb = LVM_FALSE;
451         }
452     }
453 
454     if (pPrivate->NewParams.OperatingMode != pPrivate->CurrentParams.OperatingMode) {
455         if (pPrivate->NewParams.OperatingMode == LVM_MODE_ON) {
456             pPrivate->BypassMixer.Target2 = (LVM_FLOAT)(pPrivate->NewParams.Level) / 100.0f;
457             pPrivate->BypassMixer.Target1 = 0x00000000;
458 
459             pPrivate->BypassMixer.CallbackSet2 = LVM_FALSE;
460             OperatingMode = LVM_MODE_ON;
461             if (pPrivate->NewParams.Level == 0) {
462                 pPrivate->bDisableReverb = LVM_TRUE;
463             } else {
464                 pPrivate->bDisableReverb = LVM_FALSE;
465             }
466         } else if (pPrivate->bFirstControl == LVM_FALSE) {
467             pPrivate->BypassMixer.Target2 = 0x00000000;
468             pPrivate->BypassMixer.Target1 = 0x00000000;
469             pPrivate->BypassMixer.CallbackSet2 = LVM_TRUE;
470             pPrivate->GainMixer.Target = 0.03125f;
471             OperatingMode = LVM_MODE_ON;
472         } else {
473             OperatingMode = LVM_MODE_OFF;
474         }
475     }
476 
477     /*  If it is the first call to ApplyNew settings force the current to the target \
478         to begin immediate playback of the effect */
479     if (pPrivate->bFirstControl == LVM_TRUE) {
480         pPrivate->BypassMixer.Current1 = pPrivate->BypassMixer.Target1;
481         pPrivate->BypassMixer.Current2 = pPrivate->BypassMixer.Target2;
482     }
483 
484     /*
485      * Copy the new parameters
486      */
487     pPrivate->CurrentParams = pPrivate->NewParams;
488     pPrivate->CurrentParams.OperatingMode = OperatingMode;
489 
490     /*
491      * Update flag
492      */
493     if (pPrivate->bFirstControl == LVM_TRUE) {
494         pPrivate->bFirstControl = LVM_FALSE;
495     }
496 
497     return LVREV_SUCCESS;
498 }
499 /****************************************************************************************/
500 /*                                                                                      */
501 /* FUNCTION:                BypassMixer_Callback                                        */
502 /*                                                                                      */
503 /* DESCRIPTION:                                                                         */
504 /*  Controls the On to Off operating mode transition                                    */
505 /*                                                                                      */
506 /* PARAMETERS:                                                                          */
507 /*  pPrivate                Pointer to the instance private parameters                  */
508 /*                                                                                      */
509 /* RETURNS:                                                                             */
510 /*  LVREV_Success           Succeeded                                                   */
511 /*  LVREV_NULLADDRESS       When pPrivate is NULL                                       */
512 /*                                                                                      */
513 /* NOTES:                                                                               */
514 /*                                                                                      */
515 /****************************************************************************************/
BypassMixer_Callback(void * pCallbackData,void * pGeneralPurpose,LVM_INT16 GeneralPurpose)516 LVM_INT32 BypassMixer_Callback(void* pCallbackData, void* pGeneralPurpose,
517                                LVM_INT16 GeneralPurpose) {
518     LVREV_Instance_st* pLVREV_Private = (LVREV_Instance_st*)pCallbackData;
519 
520     /*
521      * Avoid build warnings
522      */
523     (void)pGeneralPurpose;
524     (void)GeneralPurpose;
525 
526     /*
527      * Turn off
528      */
529     pLVREV_Private->CurrentParams.OperatingMode = LVM_MODE_OFF;
530     pLVREV_Private->bDisableReverb = LVM_TRUE;
531     LVREV_ClearAudioBuffers((LVREV_Handle_t)pCallbackData);
532 
533     return 0;
534 }
535 
536 /* End of file */
537