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, void* pGeneralPurpose,
36 LVM_INT16 CallbackParam);
37
38 /************************************************************************************/
39 /* */
40 /* FUNCTION: LVCS_BypassMixInit */
41 /* */
42 /* DESCRIPTION: */
43 /* Initialises the bypass mixer module */
44 /* */
45 /* The overall gain of the processed path is set by the gains in the individual */
46 /* processing blocks and by the effect level gain. */
47 /* */
48 /* The unprocessed path must have matching gain for the processed path to ensure */
49 /* as they are mixed together the correct effect is achieved, this is the value */
50 /* UnprocLoss. */
51 /* */
52 /* The overall gain is corrected by a combination of a shift with saturation and a */
53 /* linear scaler, loss. The loss ensures the sum in the mixer does not saturate */
54 /* and also corrects for any excess gain in the shift. */
55 /* */
56 /* PARAMETERS: */
57 /* hInstance Instance Handle */
58 /* pParams Initialisation parameters */
59 /* */
60 /* RETURNS: */
61 /* LVCS_Success Always succeeds */
62 /* */
63 /* NOTES: */
64 /* */
65 /************************************************************************************/
66
LVCS_BypassMixInit(LVCS_Handle_t hInstance,LVCS_Params_t * pParams)67 LVCS_ReturnStatus_en LVCS_BypassMixInit(LVCS_Handle_t hInstance, LVCS_Params_t* pParams) {
68 LVM_UINT16 Offset;
69 LVM_FLOAT Gain;
70 LVM_FLOAT Current;
71 LVCS_Instance_t* pInstance = (LVCS_Instance_t*)hInstance;
72 LVCS_BypassMix_t* pConfig = (LVCS_BypassMix_t*)&pInstance->BypassMix;
73 const Gain_t* pOutputGainTable;
74
75 /*
76 * Set the transition gain
77 */
78 if ((pParams->OperatingMode == LVCS_ON) && (pInstance->bTimerDone == LVM_TRUE) &&
79 (pInstance->MSTarget1 != 0x7FFF) /* this indicates an off->on transition */
80 ) {
81 pInstance->TransitionGain = ((LVM_FLOAT)pParams->EffectLevel / 32767);
82 } else {
83 /* Select no effect level */
84 pInstance->TransitionGain = 0;
85 }
86
87 /*
88 * Calculate the output gain table offset
89 */
90 Offset =
91 (LVM_UINT16)(pParams->SpeakerType + (pParams->SourceFormat * (1 + LVCS_EX_HEADPHONES)));
92 pOutputGainTable = (Gain_t*)&LVCS_OutputGainTable[0];
93
94 /*
95 * Setup the mixer gain for the processed path
96 */
97 Gain = (LVM_FLOAT)(pOutputGainTable[Offset].Loss * pInstance->TransitionGain);
98
99 pConfig->Mixer_Instance.MixerStream[0].CallbackParam = 0;
100 pConfig->Mixer_Instance.MixerStream[0].pCallbackHandle = LVM_NULL;
101 pConfig->Mixer_Instance.MixerStream[0].pCallBack = LVM_NULL;
102 pConfig->Mixer_Instance.MixerStream[0].CallbackSet = 1;
103
104 Current = LVC_Mixer_GetCurrent(&pConfig->Mixer_Instance.MixerStream[0]);
105 LVC_Mixer_Init(&pConfig->Mixer_Instance.MixerStream[0], (LVM_FLOAT)(Gain), Current);
106 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[0],
107 LVCS_BYPASS_MIXER_TC, pParams->SampleRate, 2);
108
109 /*
110 * Setup the mixer gain for the unprocessed path
111 */
112 Gain = (LVM_FLOAT)(pOutputGainTable[Offset].Loss *
113 (1.0 - (LVM_FLOAT)pInstance->TransitionGain));
114 Gain = (LVM_FLOAT)pOutputGainTable[Offset].UnprocLoss * Gain;
115 Current = LVC_Mixer_GetCurrent(&pConfig->Mixer_Instance.MixerStream[1]);
116 LVC_Mixer_Init(&pConfig->Mixer_Instance.MixerStream[1], (LVM_FLOAT)(Gain), Current);
117 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[1],
118 LVCS_BYPASS_MIXER_TC, pParams->SampleRate, 2);
119 pConfig->Mixer_Instance.MixerStream[1].CallbackParam = 0;
120 pConfig->Mixer_Instance.MixerStream[1].pCallbackHandle = hInstance;
121 pConfig->Mixer_Instance.MixerStream[1].CallbackSet = 1;
122 pConfig->Mixer_Instance.MixerStream[1].pCallBack = LVCS_MixerCallback;
123
124 /*
125 * Setup the output gain shift
126 */
127 pConfig->Output_Shift = pOutputGainTable[Offset].Shift;
128
129 /*
130 * Correct gain for the effect level
131 */
132 {
133 LVM_FLOAT GainCorrect;
134 LVM_FLOAT Gain1;
135 LVM_FLOAT Gain2;
136
137 Gain1 = LVC_Mixer_GetTarget(&pConfig->Mixer_Instance.MixerStream[0]);
138 Gain2 = LVC_Mixer_GetTarget(&pConfig->Mixer_Instance.MixerStream[1]);
139 /*
140 * Calculate the gain correction
141 */
142 if (pInstance->Params.CompressorMode == LVM_MODE_ON) {
143 GainCorrect = (LVM_FLOAT)(pInstance->VolCorrect.GainMin -
144 (((LVM_FLOAT)pInstance->VolCorrect.GainMin *
145 ((LVM_FLOAT)pInstance->TransitionGain))) +
146 (((LVM_FLOAT)pInstance->VolCorrect.GainFull *
147 ((LVM_FLOAT)pInstance->TransitionGain))));
148
149 /*
150 * Apply the gain correction
151 */
152 Gain1 = (Gain1 * GainCorrect);
153 Gain2 = (Gain2 * GainCorrect);
154 }
155
156 /*
157 * Set the gain values
158 */
159 pConfig->Output_Shift = pConfig->Output_Shift;
160 LVC_Mixer_SetTarget(&pConfig->Mixer_Instance.MixerStream[0], Gain1);
161 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[0],
162 LVCS_BYPASS_MIXER_TC, pParams->SampleRate, 2);
163 LVC_Mixer_SetTarget(&pConfig->Mixer_Instance.MixerStream[1], Gain2);
164 LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[1],
165 LVCS_BYPASS_MIXER_TC, pParams->SampleRate, 2);
166 }
167
168 return (LVCS_SUCCESS);
169 }
170
171 /************************************************************************************/
172 /* */
173 /* FUNCTION: LVCS_BypassMixer */
174 /* */
175 /* DESCRIPTION: */
176 /* Apply Bypass Mix. */
177 /* */
178 /* This mixes the processed and unprocessed data streams together to correct the */
179 /* overall system gain and allow progressive control of the Concert Sound effect. */
180 /* */
181 /* When the bypass mixer is enabled the output is the processed signal only and */
182 /* without gain correction. */
183 /* */
184 /* PARAMETERS: */
185 /* hInstance Instance Handle */
186 /* pProcessed Pointer to the processed data */
187 /* pUnprocessed Pointer to the unprocessed data */
188 /* pOutData Pointer to the output data */
189 /* NumSamples Number of samples to process */
190 /* */
191 /* RETURNS: */
192 /* LVCS_Success Always succeeds */
193 /* */
194 /* NOTES: */
195 /* */
196 /************************************************************************************/
197
LVCS_BypassMixer(LVCS_Handle_t hInstance,const LVM_FLOAT * pProcessed,const LVM_FLOAT * pUnprocessed,LVM_FLOAT * pOutData,LVM_UINT16 NumSamples)198 LVCS_ReturnStatus_en LVCS_BypassMixer(LVCS_Handle_t hInstance, const LVM_FLOAT* pProcessed,
199 const LVM_FLOAT* pUnprocessed, LVM_FLOAT* pOutData,
200 LVM_UINT16 NumSamples) {
201 LVCS_Instance_t* pInstance = (LVCS_Instance_t*)hInstance;
202 LVCS_BypassMix_t* pConfig = (LVCS_BypassMix_t*)&pInstance->BypassMix;
203 LVM_UINT16 destNumSamples =
204 (pInstance->Params.NrChannels == FCC_1) ? NumSamples : FCC_2 * NumSamples;
205
206 /*
207 * Check if the bypass mixer is enabled
208 */
209 if ((pInstance->Params.OperatingMode & LVCS_BYPASSMIXSWITCH) != 0) {
210 /*
211 * Apply the bypass mix
212 */
213 LVC_MixSoft_2St_D16C31_SAT(&pConfig->Mixer_Instance, pProcessed, (LVM_FLOAT*)pUnprocessed,
214 pOutData, (LVM_INT16)destNumSamples);
215 /*
216 * Apply output gain correction shift
217 */
218 Shift_Sat_Float((LVM_INT16)pConfig->Output_Shift, (LVM_FLOAT*)pOutData,
219 (LVM_FLOAT*)pOutData, (LVM_INT16)destNumSamples);
220 }
221
222 return (LVCS_SUCCESS);
223 }
224
225 /************************************************************************************/
226 /* */
227 /* FUNCTION: LVCS_MixerCallback */
228 /* */
229 /************************************************************************************/
LVCS_MixerCallback(LVCS_Handle_t hInstance,void * pGeneralPurpose,LVM_INT16 CallbackParam)230 LVM_INT32 LVCS_MixerCallback(LVCS_Handle_t hInstance, void* pGeneralPurpose,
231 LVM_INT16 CallbackParam) {
232 LVCS_Instance_t* pInstance = (LVCS_Instance_t*)hInstance;
233
234 (void)pGeneralPurpose;
235
236 /*
237 * Off transition has completed in Headphone mode
238 */
239 if ((pInstance->OutputDevice == LVCS_HEADPHONE) && (pInstance->bInOperatingModeTransition) &&
240 (pInstance->MSTarget0 == 0x0000) && /* this indicates an on->off transition */
241 (CallbackParam == 0)) {
242 /* Set operating mode to OFF */
243 pInstance->Params.OperatingMode = LVCS_OFF;
244
245 /* Exit transition state */
246 pInstance->bInOperatingModeTransition = LVM_FALSE;
247
248 /* Signal to the bundle */
249 if ((*pInstance->Capabilities.CallBack) != LVM_NULL) {
250 (*pInstance->Capabilities.CallBack)(pInstance->Capabilities.pBundleInstance, LVM_NULL,
251 (ALGORITHM_CS_ID | LVCS_EVENT_ALGOFF));
252 }
253 }
254
255 if ((pInstance->OutputDevice == LVCS_HEADPHONE) && (pInstance->MSTarget0 == 1) &&
256 (pInstance->bTimerDone == LVM_TRUE)) {
257 /* Exit transition state */
258 pInstance->bInOperatingModeTransition = LVM_FALSE;
259 }
260
261 return 1;
262 }
263