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 "LVCS_BypassMix.h"
27 #include "VectorArithmetic.h"
28 #include "LVCS_Tables.h"
29
30 /****************************************************************************************/
31 /* */
32 /* Function Prototypes */
33 /* */
34 /****************************************************************************************/
35 LVM_INT32 LVCS_MixerCallback( LVCS_Handle_t hInstance,
36 void *pGeneralPurpose,
37 LVM_INT16 CallbackParam);
38
39 /************************************************************************************/
40 /* */
41 /* FUNCTION: LVCS_BypassMixInit */
42 /* */
43 /* DESCRIPTION: */
44 /* Initialises the bypass mixer module */
45 /* */
46 /* The overall gain of the processed path is set by the gains in the individual */
47 /* processing blocks and by the effect level gain. */
48 /* */
49 /* The unprocessed path must have matching gain for the processed path to ensure */
50 /* as they are mixed together the correct effect is achieved, this is the value */
51 /* UnprocLoss. */
52 /* */
53 /* The overall gain is corrected by a combination of a shift with saturation and a */
54 /* linear scaler, loss. The loss ensures the sum in the mixer does not saturate */
55 /* and also corrects for any excess gain in the shift. */
56 /* */
57 /* PARAMETERS: */
58 /* hInstance Instance Handle */
59 /* pParams Initialisation parameters */
60 /* */
61 /* RETURNS: */
62 /* LVCS_Success Always succeeds */
63 /* */
64 /* NOTES: */
65 /* */
66 /************************************************************************************/
67
LVCS_BypassMixInit(LVCS_Handle_t hInstance,LVCS_Params_t * pParams)68 LVCS_ReturnStatus_en LVCS_BypassMixInit(LVCS_Handle_t hInstance,
69 LVCS_Params_t *pParams)
70 {
71
72 LVM_UINT16 Offset;
73 #ifndef BUILD_FLOAT
74 LVM_UINT32 Gain;
75 LVM_INT32 Current;
76 #else
77 LVM_FLOAT Gain;
78 LVM_FLOAT Current;
79 #endif
80 LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance;
81 LVCS_BypassMix_t *pConfig = (LVCS_BypassMix_t *)&pInstance->BypassMix;
82 const Gain_t *pOutputGainTable;
83
84
85
86 /*
87 * Set the transition gain
88 */
89 if ((pParams->OperatingMode == LVCS_ON) &&
90 (pInstance->bTimerDone == LVM_TRUE)
91 && (pInstance->MSTarget1 != 0x7FFF) /* this indicates an off->on transtion */
92 )
93 {
94 #ifndef BUILD_FLOAT
95 pInstance->TransitionGain = pParams->EffectLevel;
96 #else
97 pInstance->TransitionGain = ((LVM_FLOAT)pParams->EffectLevel / 32767);
98 #endif
99 }
100 else
101 {
102 /* Select no effect level */
103 pInstance->TransitionGain = 0;
104 }
105
106 /*
107 * Calculate the output gain table offset
108 */
109 Offset = (LVM_UINT16)(pParams->SpeakerType + (pParams->SourceFormat*(1+LVCS_EX_HEADPHONES)));
110 pOutputGainTable = (Gain_t*)&LVCS_OutputGainTable[0];
111
112 /*
113 * Setup the mixer gain for the processed path
114 */
115 #ifndef BUILD_FLOAT
116 Gain = (LVM_UINT32)(pOutputGainTable[Offset].Loss * pInstance->TransitionGain);
117 #else
118 Gain = (LVM_FLOAT)(pOutputGainTable[Offset].Loss * pInstance->TransitionGain);
119 #endif
120
121 pConfig->Mixer_Instance.MixerStream[0].CallbackParam = 0;
122 pConfig->Mixer_Instance.MixerStream[0].pCallbackHandle = LVM_NULL;
123 pConfig->Mixer_Instance.MixerStream[0].pCallBack = LVM_NULL;
124 pConfig->Mixer_Instance.MixerStream[0].CallbackSet=1;
125
126 #ifndef BUILD_FLOAT
127 Current = LVC_Mixer_GetCurrent(&pConfig->Mixer_Instance.MixerStream[0]);
128 LVC_Mixer_Init(&pConfig->Mixer_Instance.MixerStream[0],(LVM_INT32)(Gain >> 15),Current);
129 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[0],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
130 #else
131 Current = LVC_Mixer_GetCurrent(&pConfig->Mixer_Instance.MixerStream[0]);
132 LVC_Mixer_Init(&pConfig->Mixer_Instance.MixerStream[0], (LVM_FLOAT)(Gain), Current);
133 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[0],
134 LVCS_BYPASS_MIXER_TC, pParams->SampleRate, 2);
135 #endif
136
137 /*
138 * Setup the mixer gain for the unprocessed path
139 */
140 #ifndef BUILD_FLOAT
141 Gain = (LVM_UINT32)(pOutputGainTable[Offset].Loss * (0x7FFF - pInstance->TransitionGain));
142 Gain = (LVM_UINT32)pOutputGainTable[Offset].UnprocLoss * (Gain >> 15);
143 Current = LVC_Mixer_GetCurrent(&pConfig->Mixer_Instance.MixerStream[1]);
144 LVC_Mixer_Init(&pConfig->Mixer_Instance.MixerStream[1],(LVM_INT32)(Gain >> 15),Current);
145 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[1],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
146 #else
147 Gain = (LVM_FLOAT)(pOutputGainTable[Offset].Loss * (1.0 - \
148 (LVM_FLOAT)pInstance->TransitionGain));
149 Gain = (LVM_FLOAT)pOutputGainTable[Offset].UnprocLoss * Gain;
150 Current = LVC_Mixer_GetCurrent(&pConfig->Mixer_Instance.MixerStream[1]);
151 LVC_Mixer_Init(&pConfig->Mixer_Instance.MixerStream[1], (LVM_FLOAT)(Gain), Current);
152 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[1],
153 LVCS_BYPASS_MIXER_TC, pParams->SampleRate, 2);
154 #endif
155 pConfig->Mixer_Instance.MixerStream[1].CallbackParam = 0;
156 pConfig->Mixer_Instance.MixerStream[1].pCallbackHandle = hInstance;
157 pConfig->Mixer_Instance.MixerStream[1].CallbackSet=1;
158 pConfig->Mixer_Instance.MixerStream[1].pCallBack = LVCS_MixerCallback;
159
160 /*
161 * Setup the output gain shift
162 */
163 pConfig->Output_Shift = pOutputGainTable[Offset].Shift;
164
165
166 /*
167 * Correct gain for the effect level
168 */
169 {
170 #ifndef BUILD_FLOAT
171 LVM_INT16 GainCorrect;
172 LVM_INT32 Gain1;
173 LVM_INT32 Gain2;
174
175 Gain1 = LVC_Mixer_GetTarget(&pConfig->Mixer_Instance.MixerStream[0]);
176 Gain2 = LVC_Mixer_GetTarget(&pConfig->Mixer_Instance.MixerStream[1]);
177 /*
178 * Calculate the gain correction
179 */
180 if (pInstance->Params.CompressorMode == LVM_MODE_ON)
181 {
182 GainCorrect = (LVM_INT16)( pInstance->VolCorrect.GainMin
183 - (((LVM_INT32)pInstance->VolCorrect.GainMin * (LVM_INT32)pInstance->TransitionGain) >> 15)
184 + (((LVM_INT32)pInstance->VolCorrect.GainFull * (LVM_INT32)pInstance->TransitionGain) >> 15) );
185
186 /*
187 * Apply the gain correction and shift, note the result is in Q3.13 format
188 */
189 Gain1 = (Gain1 * GainCorrect) << 4;
190 Gain2 = (Gain2 * GainCorrect) << 4;
191 }
192 else
193 {
194 Gain1 = Gain1 << 16;
195 Gain2 = Gain2 << 16;
196 }
197
198
199
200 /*
201 * Set the gain values
202 */
203 pConfig->Output_Shift = pConfig->Output_Shift;
204 LVC_Mixer_SetTarget(&pConfig->Mixer_Instance.MixerStream[0],Gain1>>16);
205 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[0],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
206 LVC_Mixer_SetTarget(&pConfig->Mixer_Instance.MixerStream[1],Gain2>>16);
207 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[1],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
208 #else
209 LVM_FLOAT GainCorrect;
210 LVM_FLOAT Gain1;
211 LVM_FLOAT Gain2;
212
213 Gain1 = LVC_Mixer_GetTarget(&pConfig->Mixer_Instance.MixerStream[0]);
214 Gain2 = LVC_Mixer_GetTarget(&pConfig->Mixer_Instance.MixerStream[1]);
215 /*
216 * Calculate the gain correction
217 */
218 if (pInstance->Params.CompressorMode == LVM_MODE_ON)
219 {
220 GainCorrect = (LVM_FLOAT)( pInstance->VolCorrect.GainMin
221 - (((LVM_FLOAT)pInstance->VolCorrect.GainMin * \
222 ((LVM_FLOAT)pInstance->TransitionGain)))
223 + (((LVM_FLOAT)pInstance->VolCorrect.GainFull * \
224 ((LVM_FLOAT)pInstance->TransitionGain))));
225
226 /*
227 * Apply the gain correction
228 */
229 Gain1 = (Gain1 * GainCorrect);
230 Gain2 = (Gain2 * GainCorrect);
231
232 }
233
234 /*
235 * Set the gain values
236 */
237 pConfig->Output_Shift = pConfig->Output_Shift;
238 LVC_Mixer_SetTarget(&pConfig->Mixer_Instance.MixerStream[0],Gain1);
239 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[0],
240 LVCS_BYPASS_MIXER_TC, pParams->SampleRate, 2);
241 LVC_Mixer_SetTarget(&pConfig->Mixer_Instance.MixerStream[1],Gain2);
242 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[1],
243 LVCS_BYPASS_MIXER_TC, pParams->SampleRate, 2);
244 #endif
245 }
246
247 return(LVCS_SUCCESS);
248
249 }
250
251 /************************************************************************************/
252 /* */
253 /* FUNCTION: LVCS_BypassMixer */
254 /* */
255 /* DESCRIPTION: */
256 /* Apply Bypass Mix. */
257 /* */
258 /* This mixes the processed and unprocessed data streams together to correct the */
259 /* overall system gain and allow progressive control of the Concert Sound effect. */
260 /* */
261 /* When the bypass mixer is enabled the output is the processed signal only and */
262 /* without gain correction. */
263 /* */
264 /* PARAMETERS: */
265 /* hInstance Instance Handle */
266 /* pProcessed Pointer to the processed data */
267 /* pUnprocessed Pointer to the unprocessed data */
268 /* pOutData Pointer to the output data */
269 /* NumSamples Number of samples to process */
270 /* */
271 /* RETURNS: */
272 /* LVCS_Success Always succeeds */
273 /* */
274 /* NOTES: */
275 /* */
276 /************************************************************************************/
277
LVCS_BypassMixer(LVCS_Handle_t hInstance,const LVM_INT16 * pProcessed,const LVM_INT16 * pUnprocessed,LVM_INT16 * pOutData,LVM_UINT16 NumSamples)278 LVCS_ReturnStatus_en LVCS_BypassMixer(LVCS_Handle_t hInstance,
279 #ifndef BUILD_FLOAT
280 const LVM_INT16 *pProcessed,
281 const LVM_INT16 *pUnprocessed,
282 LVM_INT16 *pOutData,
283 #else
284 const LVM_FLOAT *pProcessed,
285 const LVM_FLOAT *pUnprocessed,
286 LVM_FLOAT *pOutData,
287 #endif
288 LVM_UINT16 NumSamples)
289 {
290
291 LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance;
292 LVCS_BypassMix_t *pConfig = (LVCS_BypassMix_t *)&pInstance->BypassMix;
293
294 /*
295 * Check if the bypass mixer is enabled
296 */
297 if ((pInstance->Params.OperatingMode & LVCS_BYPASSMIXSWITCH) != 0)
298 {
299 /*
300 * Apply the bypass mix
301 */
302 #ifndef BUILD_FLOAT
303 LVC_MixSoft_2St_D16C31_SAT(&pConfig->Mixer_Instance,
304 pProcessed,
305 (LVM_INT16 *) pUnprocessed,
306 pOutData,
307 (LVM_INT16)(2*NumSamples));
308
309 /*
310 * Apply output gain correction shift
311 */
312 Shift_Sat_v16xv16 ((LVM_INT16)pConfig->Output_Shift,
313 (LVM_INT16*)pOutData,
314 (LVM_INT16*)pOutData,
315 (LVM_INT16)(2*NumSamples)); /* Left and right*/
316 #else
317 LVC_MixSoft_2St_D16C31_SAT(&pConfig->Mixer_Instance,
318 pProcessed,
319 (LVM_FLOAT *) pUnprocessed,
320 pOutData,
321 (LVM_INT16)(2 * NumSamples));
322 /*
323 * Apply output gain correction shift
324 */
325 Shift_Sat_Float((LVM_INT16)pConfig->Output_Shift,
326 (LVM_FLOAT*)pOutData,
327 (LVM_FLOAT*)pOutData,
328 (LVM_INT16)(2 * NumSamples)); /* Left and right*/
329 #endif
330 }
331
332 return(LVCS_SUCCESS);
333 }
334
335
336 /************************************************************************************/
337 /* */
338 /* FUNCTION: LVCS_MixerCallback */
339 /* */
340 /************************************************************************************/
LVCS_MixerCallback(LVCS_Handle_t hInstance,void * pGeneralPurpose,LVM_INT16 CallbackParam)341 LVM_INT32 LVCS_MixerCallback(LVCS_Handle_t hInstance,
342 void *pGeneralPurpose,
343 LVM_INT16 CallbackParam)
344 {
345 LVCS_Instance_t *pInstance = (LVCS_Instance_t *)hInstance;
346
347 (void)pGeneralPurpose;
348
349 /*
350 * Off transition has completed in Headphone mode
351 */
352 if ((pInstance->OutputDevice == LVCS_HEADPHONE) &&
353 (pInstance->bInOperatingModeTransition) &&
354 (pInstance->MSTarget0 == 0x0000)&& /* this indicates an on->off transition */
355 (CallbackParam == 0))
356 {
357 /* Set operating mode to OFF */
358 pInstance->Params.OperatingMode = LVCS_OFF;
359
360 /* Exit transition state */
361 pInstance->bInOperatingModeTransition = LVM_FALSE;
362
363 /* Signal to the bundle */
364 if((*pInstance->Capabilities.CallBack) != LVM_NULL){
365 (*pInstance->Capabilities.CallBack)(pInstance->Capabilities.pBundleInstance,
366 LVM_NULL,
367 (ALGORITHM_CS_ID | LVCS_EVENT_ALGOFF));
368 }
369 }
370
371
372 if ((pInstance->OutputDevice == LVCS_HEADPHONE) &&
373 (pInstance->MSTarget0 == 1) &&
374 (pInstance->bTimerDone == LVM_TRUE)){
375
376 /* Exit transition state */
377 pInstance->bInOperatingModeTransition = LVM_FALSE;
378 }
379
380 return 1;
381 }
382
383
384
385