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/****************************************************************************************/
35LVM_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
68LVCS_ReturnStatus_en LVCS_BypassMixInit(LVCS_Handle_t       hInstance,
69                                        LVCS_Params_t       *pParams)
70{
71
72    LVM_UINT16          Offset;
73    LVM_UINT32          Gain;
74    LVCS_Instance_t     *pInstance = (LVCS_Instance_t  *)hInstance;
75    LVCS_BypassMix_t    *pConfig   = (LVCS_BypassMix_t *)&pInstance->BypassMix;
76    const Gain_t        *pOutputGainTable;
77    LVM_INT32           Current;
78
79
80    /*
81     * Set the transition gain
82     */
83    if ((pParams->OperatingMode == LVCS_ON) &&
84        (pInstance->bTimerDone == LVM_TRUE)
85        && (pInstance->MSTarget1 != 0x7FFF) /* this indicates an off->on transtion */
86        )
87    {
88        pInstance->TransitionGain = pParams->EffectLevel;
89    }
90    else
91    {
92        /* Select no effect level */
93        pInstance->TransitionGain = 0;
94    }
95
96    /*
97     * Calculate the output gain table offset
98     */
99    Offset = (LVM_UINT16)(pParams->SpeakerType + (pParams->SourceFormat*(1+LVCS_EX_HEADPHONES)));
100    pOutputGainTable = (Gain_t*)&LVCS_OutputGainTable[0];
101
102    /*
103     * Setup the mixer gain for the processed path
104     */
105    Gain = (LVM_UINT32)(pOutputGainTable[Offset].Loss * pInstance->TransitionGain);
106
107    pConfig->Mixer_Instance.MixerStream[0].CallbackParam = 0;
108    pConfig->Mixer_Instance.MixerStream[0].pCallbackHandle = LVM_NULL;
109    pConfig->Mixer_Instance.MixerStream[0].pCallBack = LVM_NULL;
110    pConfig->Mixer_Instance.MixerStream[0].CallbackSet=1;
111    Current = LVC_Mixer_GetCurrent(&pConfig->Mixer_Instance.MixerStream[0]);
112    LVC_Mixer_Init(&pConfig->Mixer_Instance.MixerStream[0],(LVM_INT32)(Gain >> 15),Current);
113    LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[0],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
114    /*
115     * Setup the mixer gain for the unprocessed path
116     */
117    Gain = (LVM_UINT32)(pOutputGainTable[Offset].Loss * (0x7FFF - pInstance->TransitionGain));
118    Gain = (LVM_UINT32)pOutputGainTable[Offset].UnprocLoss * (Gain >> 15);
119    Current = LVC_Mixer_GetCurrent(&pConfig->Mixer_Instance.MixerStream[1]);
120    LVC_Mixer_Init(&pConfig->Mixer_Instance.MixerStream[1],(LVM_INT32)(Gain >> 15),Current);
121    LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[1],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
122    pConfig->Mixer_Instance.MixerStream[1].CallbackParam = 0;
123    pConfig->Mixer_Instance.MixerStream[1].pCallbackHandle = hInstance;
124    pConfig->Mixer_Instance.MixerStream[1].CallbackSet=1;
125    pConfig->Mixer_Instance.MixerStream[1].pCallBack = LVCS_MixerCallback;
126
127    /*
128     * Setup the output gain shift
129     */
130    pConfig->Output_Shift = pOutputGainTable[Offset].Shift;
131
132
133    /*
134     * Correct gain for the effect level
135     */
136    {
137
138        LVM_INT16           GainCorrect;
139        LVM_INT32           Gain1;
140        LVM_INT32           Gain2;
141
142        Gain1 = LVC_Mixer_GetTarget(&pConfig->Mixer_Instance.MixerStream[0]);
143        Gain2 = LVC_Mixer_GetTarget(&pConfig->Mixer_Instance.MixerStream[1]);
144        /*
145         * Calculate the gain correction
146         */
147        if (pInstance->Params.CompressorMode == LVM_MODE_ON)
148        {
149        GainCorrect = (LVM_INT16)(  pInstance->VolCorrect.GainMin
150                                    - (((LVM_INT32)pInstance->VolCorrect.GainMin * (LVM_INT32)pInstance->TransitionGain) >> 15)
151                                    + (((LVM_INT32)pInstance->VolCorrect.GainFull * (LVM_INT32)pInstance->TransitionGain) >> 15) );
152
153        /*
154         * Apply the gain correction and shift, note the result is in Q3.13 format
155         */
156        Gain1 = (Gain1 * GainCorrect) << 4;
157        Gain2 = (Gain2 * GainCorrect) << 4;
158        }
159        else
160        {
161            Gain1 = Gain1 << 16;
162            Gain2 = Gain2 << 16;
163        }
164
165
166
167        /*
168         * Set the gain values
169         */
170        pConfig->Output_Shift = pConfig->Output_Shift;
171        LVC_Mixer_SetTarget(&pConfig->Mixer_Instance.MixerStream[0],Gain1>>16);
172        LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[0],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
173        LVC_Mixer_SetTarget(&pConfig->Mixer_Instance.MixerStream[1],Gain2>>16);
174        LVC_Mixer_VarSlope_SetTimeConstant(&pConfig->Mixer_Instance.MixerStream[1],LVCS_BYPASS_MIXER_TC,pParams->SampleRate,2);
175    }
176
177    return(LVCS_SUCCESS);
178
179}
180
181/************************************************************************************/
182/*                                                                                  */
183/* FUNCTION:                LVCS_BypassMixer                                        */
184/*                                                                                  */
185/* DESCRIPTION:                                                                     */
186/*  Apply Bypass Mix.                                                               */
187/*                                                                                  */
188/*  This mixes the processed and unprocessed data streams together to correct the   */
189/*  overall system gain and allow progressive control of the Concert Sound effect.  */
190/*                                                                                  */
191/*  When the bypass mixer is enabled the output is the processed signal only and    */
192/*  without gain correction.                                                        */
193/*                                                                                  */
194/* PARAMETERS:                                                                      */
195/*  hInstance               Instance Handle                                         */
196/*  pProcessed              Pointer to the processed data                           */
197/*  pUnprocessed            Pointer to the unprocessed data                         */
198/*  pOutData                Pointer to the output data                              */
199/*  NumSamples              Number of samples to process                            */
200/*                                                                                  */
201/* RETURNS:                                                                         */
202/*  LVCS_Success            Always succeeds                                         */
203/*                                                                                  */
204/* NOTES:                                                                           */
205/*                                                                                  */
206/************************************************************************************/
207
208LVCS_ReturnStatus_en LVCS_BypassMixer(LVCS_Handle_t         hInstance,
209                                      const LVM_INT16       *pProcessed,
210                                      const LVM_INT16       *pUnprocessed,
211                                      LVM_INT16             *pOutData,
212                                      LVM_UINT16            NumSamples)
213{
214
215    LVCS_Instance_t     *pInstance      = (LVCS_Instance_t  *)hInstance;
216    LVCS_BypassMix_t    *pConfig        = (LVCS_BypassMix_t *)&pInstance->BypassMix;
217
218    /*
219     * Check if the bypass mixer is enabled
220     */
221    if ((pInstance->Params.OperatingMode & LVCS_BYPASSMIXSWITCH) != 0)
222    {
223        /*
224         * Apply the bypass mix
225         */
226        LVC_MixSoft_2St_D16C31_SAT(&pConfig->Mixer_Instance,
227                                        pProcessed,
228                                        (LVM_INT16 *) pUnprocessed,
229                                        pOutData,
230                                        (LVM_INT16)(2*NumSamples));
231
232        /*
233         * Apply output gain correction shift
234         */
235        Shift_Sat_v16xv16 ((LVM_INT16)pConfig->Output_Shift,
236                          (LVM_INT16*)pOutData,
237                          (LVM_INT16*)pOutData,
238                          (LVM_INT16)(2*NumSamples));          /* Left and right*/
239    }
240
241    return(LVCS_SUCCESS);
242}
243
244
245/************************************************************************************/
246/*                                                                                  */
247/* FUNCTION:                LVCS_MixerCallback                                      */
248/*                                                                                  */
249/************************************************************************************/
250LVM_INT32 LVCS_MixerCallback(LVCS_Handle_t      hInstance,
251                            void                *pGeneralPurpose,
252                            LVM_INT16           CallbackParam)
253{
254    LVCS_Instance_t     *pInstance = (LVCS_Instance_t  *)hInstance;
255
256   (void)pGeneralPurpose;
257
258    /*
259     * Off transition has completed in Headphone mode
260     */
261    if ((pInstance->OutputDevice == LVCS_HEADPHONE) &&
262        (pInstance->bInOperatingModeTransition)     &&
263        (pInstance->MSTarget0 == 0x0000)&&  /* this indicates an on->off transition */
264        (CallbackParam == 0))
265    {
266        /* Set operating mode to OFF */
267        pInstance->Params.OperatingMode = LVCS_OFF;
268
269        /* Exit transition state */
270        pInstance->bInOperatingModeTransition = LVM_FALSE;
271
272        /* Signal to the bundle */
273        if((*pInstance->Capabilities.CallBack) != LVM_NULL){
274            (*pInstance->Capabilities.CallBack)(pInstance->Capabilities.pBundleInstance,
275                                                LVM_NULL,
276                                                (ALGORITHM_CS_ID | LVCS_EVENT_ALGOFF));
277        }
278    }
279
280
281    if ((pInstance->OutputDevice == LVCS_HEADPHONE)  &&
282        (pInstance->MSTarget0 == 1) &&
283        (pInstance->bTimerDone == LVM_TRUE)){
284
285        /* Exit transition state */
286        pInstance->bInOperatingModeTransition = LVM_FALSE;
287    }
288
289    return 1;
290}
291
292
293
294