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 "LVCS.h"
25 #include "LVCS_Private.h"
26 #include "VectorArithmetic.h"
27 #include "CompLim.h"
28
29 /************************************************************************************/
30 /* */
31 /* FUNCTION: LVCS_Process_CS */
32 /* */
33 /* DESCRIPTION: */
34 /* Process function for the Concert Sound module based on the following block */
35 /* diagram: */
36 /* _________ ________ _____ _______ ___ ______ */
37 /* | | | | | | | | | | | | */
38 /* ----->| Stereo |->| Reverb |->| Equ |->| Alpha |-->| + |-| Gain |----> */
39 /* | | Enhance | |________| |_____| |_______| |___| |______| */
40 /* | |_________| | */
41 /* | ___________ | */
42 /* | | | | */
43 /* |------------------------------->| 1 - Alpha |-----| */
44 /* |___________| */
45 /* */
46 /* The Stereo Enhancer, Reverb and Equaliser blocks are each configured to have */
47 /* their gain to give a near peak to peak output (-0.1dBFS) with a worst case */
48 /* input signal. The gains of these blocks are re-combined in the Alpha mixer and */
49 /* the gain block folloing the sum. */
50 /* */
51 /* The processing uses the output buffer for data storage after each processing */
52 /* block. When processing is inplace a copy of the input signal is made in scratch */
53 /* memory for the 1-Alpha path. */
54 /* */
55 /* */
56 /* PARAMETERS: */
57 /* hInstance Instance handle */
58 /* pInData Pointer to the input data */
59 /* pOutData Pointer to the output data */
60 /* NumSamples Number of samples in the input buffer */
61 /* */
62 /* RETURNS: */
63 /* LVCS_Success Succeeded */
64 /* */
65 /* NOTES: */
66 /* */
67 /************************************************************************************/
LVCS_Process_CS(LVCS_Handle_t hInstance,const LVM_FLOAT * pInData,LVM_FLOAT * pOutData,LVM_UINT16 NumSamples)68 LVCS_ReturnStatus_en LVCS_Process_CS(LVCS_Handle_t hInstance,
69 const LVM_FLOAT *pInData,
70 LVM_FLOAT *pOutData,
71 LVM_UINT16 NumSamples)
72 {
73 const LVM_FLOAT *pInput;
74 LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance;
75 LVM_FLOAT *pScratch;
76 LVCS_ReturnStatus_en err;
77 #ifdef SUPPORT_MC
78 LVM_FLOAT *pStIn;
79 LVM_INT32 channels = pInstance->Params.NrChannels;
80 #define NrFrames NumSamples // alias for clarity
81
82 /*In case of mono processing, stereo input is created from mono
83 *and stored in pInData before applying any of the effects.
84 *However we do not update the value pInstance->Params.NrChannels
85 *at this point.
86 *So to treat the pInData as stereo we are setting channels to 2
87 */
88 if (channels == 1)
89 {
90 channels = 2;
91 }
92 #endif
93
94 pScratch = (LVM_FLOAT *) \
95 pInstance->MemoryTable.Region[LVCS_MEMREGION_TEMPORARY_FAST].pBaseAddress;
96
97 /*
98 * Check if the processing is inplace
99 */
100 #ifdef SUPPORT_MC
101 /*
102 * The pInput buffer holds the first 2 (Left, Right) channels information.
103 * Hence the memory required by this buffer is 2 * NumFrames.
104 * The Concert Surround module carries out processing only on L, R.
105 */
106 pInput = pScratch + (2 * NrFrames);
107 pStIn = pScratch + ((LVCS_SCRATCHBUFFERS - 2) * NrFrames);
108 /* The first two channel data is extracted from the input data and
109 * copied into pInput buffer
110 */
111 Copy_Float_Mc_Stereo((LVM_FLOAT *)pInData,
112 (LVM_FLOAT *)pInput,
113 NrFrames,
114 channels);
115 Copy_Float((LVM_FLOAT *)pInput,
116 (LVM_FLOAT *)pStIn,
117 (LVM_INT16)(2 * NrFrames));
118 #else
119 if (pInData == pOutData)
120 {
121 /* Processing inplace */
122 pInput = pScratch + (2 * NumSamples);
123 Copy_Float((LVM_FLOAT *)pInData, /* Source */
124 (LVM_FLOAT *)pInput, /* Destination */
125 (LVM_INT16)(2 * NumSamples)); /* Left and right */
126 }
127 else
128 {
129 /* Processing outplace */
130 pInput = pInData;
131 }
132 #endif
133 /*
134 * Call the stereo enhancer
135 */
136 #ifdef SUPPORT_MC
137 err = LVCS_StereoEnhancer(hInstance, /* Instance handle */
138 pStIn, /* Pointer to the input data */
139 pOutData, /* Pointer to the output data */
140 NrFrames); /* Number of frames to process */
141 #else
142 err = LVCS_StereoEnhancer(hInstance, /* Instance handle */
143 pInData, /* Pointer to the input data */
144 pOutData, /* Pointer to the output data */
145 NumSamples); /* Number of samples to process */
146 #endif
147
148 /*
149 * Call the reverb generator
150 */
151 err = LVCS_ReverbGenerator(hInstance, /* Instance handle */
152 pOutData, /* Pointer to the input data */
153 pOutData, /* Pointer to the output data */
154 NumSamples); /* Number of samples to process */
155
156 /*
157 * Call the equaliser
158 */
159 err = LVCS_Equaliser(hInstance, /* Instance handle */
160 pOutData, /* Pointer to the input data */
161 NumSamples); /* Number of samples to process */
162
163 /*
164 * Call the bypass mixer
165 */
166 err = LVCS_BypassMixer(hInstance, /* Instance handle */
167 pOutData, /* Pointer to the processed data */
168 pInput, /* Pointer to the input (unprocessed) data */
169 pOutData, /* Pointer to the output data */
170 NumSamples); /* Number of samples to process */
171
172 if(err != LVCS_SUCCESS)
173 {
174 return err;
175 }
176
177 return(LVCS_SUCCESS);
178 }
179 /************************************************************************************/
180 /* */
181 /* FUNCTION: LVCS_Process */
182 /* */
183 /* DESCRIPTION: */
184 /* Process function for the Concert Sound module. The implementation supports two */
185 /* variants of the algorithm, one for headphones and one for mobile speakers. */
186 /* */
187 /* Data can be processed in two formats, stereo or mono-in-stereo. Data in mono */
188 /* format is not supported, the calling routine must convert the mono stream to */
189 /* mono-in-stereo. */
190 /* */
191 /* */
192 /* PARAMETERS: */
193 /* hInstance Instance handle */
194 /* pInData Pointer to the input data */
195 /* pOutData Pointer to the output data */
196 /* NumSamples Number of samples in the input buffer */
197 /* */
198 /* RETURNS: */
199 /* LVCS_Success Succeeded */
200 /* LVCS_TooManySamples NumSamples was larger than the maximum block size */
201 /* */
202 /* NOTES: */
203 /* */
204 /************************************************************************************/
LVCS_Process(LVCS_Handle_t hInstance,const LVM_FLOAT * pInData,LVM_FLOAT * pOutData,LVM_UINT16 NumSamples)205 LVCS_ReturnStatus_en LVCS_Process(LVCS_Handle_t hInstance,
206 const LVM_FLOAT *pInData,
207 LVM_FLOAT *pOutData,
208 LVM_UINT16 NumSamples)
209 {
210
211 LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance;
212 LVCS_ReturnStatus_en err;
213 #ifdef SUPPORT_MC
214 /*Extract number of Channels info*/
215 LVM_INT32 channels = pInstance->Params.NrChannels;
216 #define NrFrames NumSamples // alias for clarity
217 if (channels == 1)
218 {
219 channels = 2;
220 }
221 #endif
222 /*
223 * Check the number of samples is not too large
224 */
225 if (NumSamples > pInstance->Capabilities.MaxBlockSize)
226 {
227 return(LVCS_TOOMANYSAMPLES);
228 }
229
230 /*
231 * Check if the algorithm is enabled
232 */
233 if (pInstance->Params.OperatingMode != LVCS_OFF)
234 {
235 #ifdef SUPPORT_MC
236 LVM_FLOAT *pStereoOut;
237 /*
238 * LVCS_Process_CS uses output buffer to store intermediate outputs of StereoEnhancer,
239 * Equalizer, ReverbGenerator and BypassMixer.
240 * So, to avoid i/o data overlapping, when i/o buffers are common, use scratch buffer
241 * to store intermediate outputs.
242 */
243 if (pOutData == pInData)
244 {
245 /*
246 * Scratch memory is used in 4 chunks of (2 * NrFrames) size.
247 * First chunk of memory is used by LVCS_StereoEnhancer and LVCS_ReverbGenerator,
248 * second and fourth are used as input buffers by pInput and pStIn in LVCS_Process_CS.
249 * Hence, pStereoOut is pointed to use unused third portion of scratch memory.
250 */
251 pStereoOut = (LVM_FLOAT *) \
252 pInstance->MemoryTable. \
253 Region[LVCS_MEMREGION_TEMPORARY_FAST].pBaseAddress +
254 ((LVCS_SCRATCHBUFFERS - 4) * NrFrames);
255 }
256 else
257 {
258 pStereoOut = pOutData;
259 }
260
261 /*
262 * Call CS process function
263 */
264 err = LVCS_Process_CS(hInstance,
265 pInData,
266 pStereoOut,
267 NrFrames);
268 #else
269 err = LVCS_Process_CS(hInstance,
270 pInData,
271 pOutData,
272 NumSamples);
273 #endif
274
275 /*
276 * Compress to reduce expansion effect of Concert Sound and correct volume
277 * differences for difference settings. Not applied in test modes
278 */
279 if ((pInstance->Params.OperatingMode == LVCS_ON)&& \
280 (pInstance->Params.CompressorMode == LVM_MODE_ON))
281 {
282 LVM_FLOAT Gain = pInstance->VolCorrect.CompMin;
283 LVM_FLOAT Current1;
284
285 Current1 = LVC_Mixer_GetCurrent(&pInstance->BypassMix.Mixer_Instance.MixerStream[0]);
286 Gain = (LVM_FLOAT)( pInstance->VolCorrect.CompMin
287 - (((LVM_FLOAT)pInstance->VolCorrect.CompMin * (Current1)))
288 + (((LVM_FLOAT)pInstance->VolCorrect.CompFull * (Current1))));
289
290 if(NumSamples < LVCS_COMPGAINFRAME)
291 {
292 #ifdef SUPPORT_MC
293 NonLinComp_Float(Gain, /* Compressor gain setting */
294 pStereoOut,
295 pStereoOut,
296 (LVM_INT32)(2 * NrFrames));
297 #else
298 NonLinComp_Float(Gain, /* Compressor gain setting */
299 pOutData,
300 pOutData,
301 (LVM_INT32)(2 * NumSamples));
302 #endif
303 }
304 else
305 {
306 LVM_FLOAT GainStep;
307 LVM_FLOAT FinalGain;
308 LVM_INT16 SampleToProcess = NumSamples;
309 LVM_FLOAT *pOutPtr;
310
311 /* Large changes in Gain can cause clicks in output
312 Split data into small blocks and use interpolated gain values */
313
314 GainStep = (LVM_FLOAT)(((Gain-pInstance->CompressGain) * \
315 LVCS_COMPGAINFRAME) / NumSamples);
316
317 if((GainStep == 0) && (pInstance->CompressGain < Gain))
318 {
319 GainStep = 1;
320 }
321 else
322 {
323 if((GainStep == 0) && (pInstance->CompressGain > Gain))
324 {
325 GainStep = -1;
326 }
327 }
328
329 FinalGain = Gain;
330 Gain = pInstance->CompressGain;
331 #ifdef SUPPORT_MC
332 pOutPtr = pStereoOut;
333 #else
334 pOutPtr = pOutData;
335 #endif
336
337 while(SampleToProcess > 0)
338 {
339 Gain = (LVM_FLOAT)(Gain + GainStep);
340 if((GainStep > 0) && (FinalGain <= Gain))
341 {
342 Gain = FinalGain;
343 GainStep = 0;
344 }
345
346 if((GainStep < 0) && (FinalGain > Gain))
347 {
348 Gain = FinalGain;
349 GainStep = 0;
350 }
351
352 if(SampleToProcess > LVCS_COMPGAINFRAME)
353 {
354 NonLinComp_Float(Gain, /* Compressor gain setting */
355 pOutPtr,
356 pOutPtr,
357 (LVM_INT32)(2 * LVCS_COMPGAINFRAME));
358 pOutPtr += (2 * LVCS_COMPGAINFRAME);
359 SampleToProcess = (LVM_INT16)(SampleToProcess - LVCS_COMPGAINFRAME);
360 }
361 else
362 {
363 NonLinComp_Float(Gain, /* Compressor gain setting */
364 pOutPtr,
365 pOutPtr,
366 (LVM_INT32)(2 * SampleToProcess));
367 SampleToProcess = 0;
368 }
369
370 }
371 }
372
373 /* Store gain value*/
374 pInstance->CompressGain = Gain;
375 }
376
377 if(pInstance->bInOperatingModeTransition == LVM_TRUE){
378
379 /*
380 * Re-init bypass mix when timer has completed
381 */
382 if ((pInstance->bTimerDone == LVM_TRUE) &&
383 (pInstance->BypassMix.Mixer_Instance.MixerStream[1].CallbackSet == 0))
384 {
385 err = LVCS_BypassMixInit(hInstance,
386 &pInstance->Params);
387
388 if(err != LVCS_SUCCESS)
389 {
390 return err;
391 }
392
393 }
394 else{
395 LVM_Timer ( &pInstance->TimerInstance,
396 (LVM_INT16)NumSamples);
397 }
398 }
399 #ifdef SUPPORT_MC
400 Copy_Float_Stereo_Mc(pInData,
401 pStereoOut,
402 pOutData,
403 NrFrames,
404 channels);
405 #endif
406 }
407 else
408 {
409 if (pInData != pOutData)
410 {
411 #ifdef SUPPORT_MC
412 /*
413 * The algorithm is disabled so just copy the data
414 */
415 Copy_Float((LVM_FLOAT *)pInData, /* Source */
416 (LVM_FLOAT *)pOutData, /* Destination */
417 (LVM_INT16)(channels * NrFrames)); /* All Channels*/
418 #else
419 /*
420 * The algorithm is disabled so just copy the data
421 */
422 Copy_Float((LVM_FLOAT *)pInData, /* Source */
423 (LVM_FLOAT *)pOutData, /* Destination */
424 (LVM_INT16)(2 * NumSamples)); /* Left and right */
425 #endif
426 }
427 }
428
429 return(LVCS_SUCCESS);
430 }
431