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, const LVM_FLOAT* pInData,
69 LVM_FLOAT* pOutData, LVM_UINT16 NumSamples) {
70 const LVM_FLOAT* pInput;
71 LVCS_Instance_t* pInstance = (LVCS_Instance_t*)hInstance;
72 LVM_FLOAT* pScratch;
73 LVCS_ReturnStatus_en err;
74 LVM_FLOAT* pStIn;
75 LVM_INT32 channels = pInstance->Params.NrChannels;
76 #define NrFrames NumSamples // alias for clarity
77
78 pScratch = (LVM_FLOAT*)pInstance->pScratch;
79
80 /*
81 * Check if the processing is inplace
82 */
83 /*
84 * The pInput buffer holds the first 2 (Left, Right) channels information.
85 * Hence the memory required by this buffer is 2 * NumFrames.
86 * The Concert Surround module carries out processing only on L, R.
87 */
88 pInput = pScratch + (2 * NrFrames);
89 pStIn = pScratch + ((LVCS_SCRATCHBUFFERS - 2) * NrFrames);
90 if (channels == FCC_1) {
91 Copy_Float((LVM_FLOAT*)pInData, (LVM_FLOAT*)pInput, (LVM_INT16)NrFrames);
92 Copy_Float((LVM_FLOAT*)pInput, (LVM_FLOAT*)pStIn, (LVM_INT16)NrFrames);
93 } else {
94 /* The first two channel data is extracted from the input data and
95 * copied into pInput buffer
96 */
97 Copy_Float_Mc_Stereo((LVM_FLOAT*)pInData, (LVM_FLOAT*)pInput, NrFrames, channels);
98 Copy_Float((LVM_FLOAT*)pInput, (LVM_FLOAT*)pStIn, (LVM_INT16)(FCC_2 * NrFrames));
99 }
100 /*
101 * Call the stereo enhancer
102 */
103 err = LVCS_StereoEnhancer(hInstance, /* Instance handle */
104 pStIn, /* Pointer to the input data */
105 pOutData, /* Pointer to the output data */
106 NrFrames); /* Number of frames to process */
107
108 /*
109 * Call the reverb generator
110 */
111 err = LVCS_ReverbGenerator(hInstance, /* Instance handle */
112 pOutData, /* Pointer to the input data */
113 pOutData, /* Pointer to the output data */
114 NumSamples); /* Number of samples to process */
115
116 /*
117 * Call the equaliser
118 */
119 err = LVCS_Equaliser(hInstance, /* Instance handle */
120 pOutData, /* Pointer to the input data */
121 NumSamples); /* Number of samples to process */
122
123 /*
124 * Call the bypass mixer
125 */
126 err = LVCS_BypassMixer(hInstance, /* Instance handle */
127 pOutData, /* Pointer to the processed data */
128 pInput, /* Pointer to the input (unprocessed) data */
129 pOutData, /* Pointer to the output data */
130 NumSamples); /* Number of samples to process */
131
132 if (err != LVCS_SUCCESS) {
133 return err;
134 }
135
136 return (LVCS_SUCCESS);
137 }
138 /************************************************************************************/
139 /* */
140 /* FUNCTION: LVCS_Process */
141 /* */
142 /* DESCRIPTION: */
143 /* Process function for the Concert Sound module. The implementation supports two */
144 /* variants of the algorithm, one for headphones and one for mobile speakers. */
145 /* */
146 /* Data can be processed in two formats, stereo or mono-in-stereo. Data in mono */
147 /* format is not supported, the calling routine must convert the mono stream to */
148 /* mono-in-stereo. */
149 /* */
150 /* */
151 /* PARAMETERS: */
152 /* hInstance Instance handle */
153 /* pInData Pointer to the input data */
154 /* pOutData Pointer to the output data */
155 /* NumSamples Number of samples in the input buffer */
156 /* */
157 /* RETURNS: */
158 /* LVCS_Success Succeeded */
159 /* LVCS_TooManySamples NumSamples was larger than the maximum block size */
160 /* */
161 /* NOTES: */
162 /* */
163 /************************************************************************************/
LVCS_Process(LVCS_Handle_t hInstance,const LVM_FLOAT * pInData,LVM_FLOAT * pOutData,LVM_UINT16 NumSamples)164 LVCS_ReturnStatus_en LVCS_Process(LVCS_Handle_t hInstance, const LVM_FLOAT* pInData,
165 LVM_FLOAT* pOutData, LVM_UINT16 NumSamples) {
166 LVCS_Instance_t* pInstance = (LVCS_Instance_t*)hInstance;
167 LVCS_ReturnStatus_en err;
168 /*Extract number of Channels info*/
169 LVM_INT32 channels = pInstance->Params.NrChannels;
170 LVM_UINT16 destNumSamples = (channels == FCC_1) ? NumSamples : FCC_2 * NumSamples;
171 LVM_INT32 compGainInterval =
172 (channels == FCC_1) ? LVCS_COMPGAINFRAME : FCC_2 * LVCS_COMPGAINFRAME;
173 #define NrFrames NumSamples // alias for clarity
174 /*
175 * Check the number of samples is not too large
176 */
177 if (NumSamples > pInstance->Capabilities.MaxBlockSize) {
178 return (LVCS_TOOMANYSAMPLES);
179 }
180
181 /*
182 * Check if the algorithm is enabled
183 */
184 if (pInstance->Params.OperatingMode != LVCS_OFF) {
185 LVM_FLOAT* pStereoOut;
186 /*
187 * LVCS_Process_CS uses output buffer to store intermediate outputs of StereoEnhancer,
188 * Equalizer, ReverbGenerator and BypassMixer.
189 * So, to avoid i/o data overlapping, when i/o buffers are common, use scratch buffer
190 * to store intermediate outputs.
191 */
192 if (pOutData == pInData) {
193 /*
194 * Scratch memory is used in 4 chunks of (2 * NrFrames) size.
195 * First chunk of memory is used by LVCS_StereoEnhancer and LVCS_ReverbGenerator,
196 * second and fourth are used as input buffers by pInput and pStIn in LVCS_Process_CS.
197 * Hence, pStereoOut is pointed to use unused third portion of scratch memory.
198 */
199 pStereoOut = (LVM_FLOAT*)pInstance->pScratch + ((LVCS_SCRATCHBUFFERS - 4) * NrFrames);
200 } else {
201 pStereoOut = pOutData;
202 }
203
204 /*
205 * Call CS process function
206 */
207 err = LVCS_Process_CS(hInstance, pInData, pStereoOut, NrFrames);
208
209 /*
210 * Compress to reduce expansion effect of Concert Sound and correct volume
211 * differences for difference settings. Not applied in test modes
212 */
213 if ((pInstance->Params.OperatingMode == LVCS_ON) &&
214 (pInstance->Params.CompressorMode == LVM_MODE_ON)) {
215 LVM_FLOAT Gain = pInstance->VolCorrect.CompMin;
216 LVM_FLOAT Current1;
217
218 Current1 = LVC_Mixer_GetCurrent(&pInstance->BypassMix.Mixer_Instance.MixerStream[0]);
219 Gain = (LVM_FLOAT)(pInstance->VolCorrect.CompMin -
220 (((LVM_FLOAT)pInstance->VolCorrect.CompMin * (Current1))) +
221 (((LVM_FLOAT)pInstance->VolCorrect.CompFull * (Current1))));
222
223 if (NumSamples < LVCS_COMPGAINFRAME) {
224 NonLinComp_Float(Gain, /* Compressor gain setting */
225 pStereoOut, pStereoOut, (LVM_INT32)destNumSamples);
226 } else {
227 LVM_FLOAT GainStep;
228 LVM_FLOAT FinalGain;
229 LVM_INT16 SampleToProcess = NumSamples;
230 LVM_FLOAT* pOutPtr;
231
232 /* Large changes in Gain can cause clicks in output
233 Split data into small blocks and use interpolated gain values */
234
235 GainStep = (LVM_FLOAT)(((Gain - pInstance->CompressGain) * LVCS_COMPGAINFRAME) /
236 NumSamples);
237
238 if ((GainStep == 0) && (pInstance->CompressGain < Gain)) {
239 GainStep = 1;
240 } else {
241 if ((GainStep == 0) && (pInstance->CompressGain > Gain)) {
242 GainStep = -1;
243 }
244 }
245
246 FinalGain = Gain;
247 Gain = pInstance->CompressGain;
248 pOutPtr = pStereoOut;
249
250 while (SampleToProcess > 0) {
251 Gain = (LVM_FLOAT)(Gain + GainStep);
252 if ((GainStep > 0) && (FinalGain <= Gain)) {
253 Gain = FinalGain;
254 GainStep = 0;
255 }
256
257 if ((GainStep < 0) && (FinalGain > Gain)) {
258 Gain = FinalGain;
259 GainStep = 0;
260 }
261
262 if (SampleToProcess > LVCS_COMPGAINFRAME) {
263 NonLinComp_Float(Gain, /* Compressor gain setting */
264 pOutPtr, pOutPtr, compGainInterval);
265 pOutPtr += compGainInterval;
266 SampleToProcess = (LVM_INT16)(SampleToProcess - LVCS_COMPGAINFRAME);
267 } else {
268 NonLinComp_Float(Gain, /* Compressor gain setting */
269 pOutPtr, pOutPtr,
270 (channels == FCC_1)
271 ? (LVM_INT32)(SampleToProcess)
272 : (LVM_INT32)(FCC_2 * SampleToProcess));
273 SampleToProcess = 0;
274 }
275 }
276 }
277
278 /* Store gain value*/
279 pInstance->CompressGain = Gain;
280 }
281
282 if (pInstance->bInOperatingModeTransition == LVM_TRUE) {
283 /*
284 * Re-init bypass mix when timer has completed
285 */
286 if ((pInstance->bTimerDone == LVM_TRUE) &&
287 (pInstance->BypassMix.Mixer_Instance.MixerStream[1].CallbackSet == 0)) {
288 err = LVCS_BypassMixInit(hInstance, &pInstance->Params);
289
290 if (err != LVCS_SUCCESS) {
291 return err;
292 }
293
294 } else {
295 LVM_Timer(&pInstance->TimerInstance, (LVM_INT16)NumSamples);
296 }
297 }
298 Copy_Float_Stereo_Mc(pInData, (const LVM_FLOAT*)pStereoOut, pOutData, NrFrames, channels);
299 } else {
300 if (pInData != pOutData) {
301 /*
302 * The algorithm is disabled so just copy the data
303 */
304 Copy_Float((LVM_FLOAT*)pInData, /* Source */
305 (LVM_FLOAT*)pOutData, /* Destination */
306 (LVM_INT16)(channels * NrFrames)); /* All Channels*/
307 }
308 }
309
310 return (LVCS_SUCCESS);
311 }
312