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 "AGC.h"
25#include "ScalarArithmetic.h"
26
27
28/****************************************************************************************/
29/*                                                                                      */
30/*    Defines                                                                           */
31/*                                                                                      */
32/****************************************************************************************/
33
34#define VOL_TC_SHIFT                                        21          /* As a power of 2 */
35#define DECAY_SHIFT                                        10           /* As a power of 2 */
36#ifdef BUILD_FLOAT
37#define VOL_TC_FLOAT                                      2.0f          /* As a power of 2 */
38#define DECAY_FAC_FLOAT                                  64.0f          /* As a power of 2 */
39#endif
40
41/****************************************************************************************/
42/*                                                                                      */
43/* FUNCTION:                  AGC_MIX_VOL_2St1Mon_D32_WRA                               */
44/*                                                                                      */
45/* DESCRIPTION:                                                                         */
46/*    Apply AGC and mix signals                                                         */
47/*                                                                                      */
48/*                                                                                      */
49/*  StSrc   ------------------|                                                         */
50/*                            |                                                         */
51/*              ______       _|_        ________                                        */
52/*             |      |     |   |      |        |                                       */
53/*  MonoSrc -->| AGC  |---->| + |----->| Volume |------------------------------+--->    */
54/*             | Gain |     |___|      | Gain   |                              |        */
55/*             |______|                |________|                              |        */
56/*                /|\                               __________     ________    |        */
57/*                 |                               |          |   |        |   |        */
58/*                 |-------------------------------| AGC Gain |<--| Peak   |<--|        */
59/*                                                 | Update   |   | Detect |            */
60/*                                                 |__________|   |________|            */
61/*                                                                                      */
62/*                                                                                      */
63/* PARAMETERS:                                                                          */
64/*  pInstance               Instance pointer                                            */
65/*  pStereoIn               Stereo source                                               */
66/*  pMonoIn                 Mono band pass source                                       */
67/*  pStereoOut              Stereo destination                                          */
68/*                                                                                      */
69/* RETURNS:                                                                             */
70/*  Void                                                                                */
71/*                                                                                      */
72/* NOTES:                                                                               */
73/*                                                                                      */
74/****************************************************************************************/
75#ifndef BUILD_FLOAT
76void AGC_MIX_VOL_2St1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_D32_t  *pInstance,     /* Instance pointer */
77                                 const LVM_INT32            *pStSrc,        /* Stereo source */
78                                 const LVM_INT32            *pMonoSrc,      /* Mono source */
79                                 LVM_INT32                  *pDst,          /* Stereo destination */
80                                 LVM_UINT16                 NumSamples)     /* Number of samples */
81{
82
83    /*
84     * General variables
85     */
86    LVM_UINT16      i;                                          /* Sample index */
87    LVM_INT32       Left;                                       /* Left sample */
88    LVM_INT32       Right;                                      /* Right sample */
89    LVM_INT32       Mono;                                       /* Mono sample */
90    LVM_INT32       AbsPeak;                                    /* Absolute peak signal */
91    LVM_INT32       HighWord;                                   /* High word in intermediate calculations */
92    LVM_INT32       LowWord;                                    /* Low word in intermediate calculations */
93    LVM_INT16       AGC_Mult;                                   /* Short AGC gain */
94    LVM_INT16       Vol_Mult;                                   /* Short volume */
95
96
97    /*
98     * Instance control variables
99     */
100    LVM_INT32      AGC_Gain      = pInstance->AGC_Gain;         /* Get the current AGC gain */
101    LVM_INT32      AGC_MaxGain   = pInstance->AGC_MaxGain;      /* Get maximum AGC gain */
102    LVM_INT16      AGC_GainShift = pInstance->AGC_GainShift;    /* Get the AGC shift */
103    LVM_INT16      AGC_Attack    = pInstance->AGC_Attack;       /* Attack scaler */
104    LVM_INT16      AGC_Decay     = pInstance->AGC_Decay;        /* Decay scaler */
105    LVM_INT32      AGC_Target    = pInstance->AGC_Target;       /* Get the target level */
106    LVM_INT32      Vol_Current   = pInstance->Volume;           /* Actual volume setting */
107    LVM_INT32      Vol_Target    = pInstance->Target;           /* Target volume setting */
108    LVM_INT16      Vol_Shift     = pInstance->VolumeShift;      /* Volume shift scaling */
109    LVM_INT16      Vol_TC        = pInstance->VolumeTC;         /* Time constant */
110
111
112    /*
113     * Process on a sample by sample basis
114     */
115    for (i=0;i<NumSamples;i++)                                  /* For each sample */
116    {
117
118        /*
119         * Get the short scalers
120         */
121        AGC_Mult    = (LVM_INT16)(AGC_Gain >> 16);              /* Get the short AGC gain */
122        Vol_Mult    = (LVM_INT16)(Vol_Current >> 16);           /* Get the short volume gain */
123
124
125        /*
126         * Get the input samples
127         */
128        Left  = *pStSrc++;                                      /* Get the left sample */
129        Right = *pStSrc++;                                      /* Get the right sample */
130        Mono  = *pMonoSrc++;                                    /* Get the mono sample */
131
132
133        /*
134         * Apply the AGC gain to the mono input and mix with the stereo signal
135         */
136        HighWord = (AGC_Mult * (Mono >> 16));                   /* signed long (Mono) by unsigned short (AGC_Mult) multiply */
137        LowWord = (AGC_Mult * (Mono & 0xffff));
138        Mono = (HighWord + (LowWord >> 16)) << (AGC_GainShift);
139        Left  += Mono;                                          /* Mix in the mono signal */
140        Right += Mono;
141
142
143        /*
144         * Apply the volume and write to the output stream
145         */
146        HighWord = (Vol_Mult * (Left >> 16));                   /* signed long (Left) by unsigned short (Vol_Mult) multiply */
147        LowWord = (Vol_Mult * (Left & 0xffff));
148        Left = (HighWord + (LowWord >> 16)) << (Vol_Shift);
149        HighWord = (Vol_Mult * (Right >> 16));                  /* signed long (Right) by unsigned short (Vol_Mult) multiply */
150        LowWord = (Vol_Mult * (Right & 0xffff));
151        Right = (HighWord + (LowWord >> 16)) << (Vol_Shift);
152        *pDst++ = Left;                                         /* Save the results */
153        *pDst++ = Right;
154
155
156        /*
157         * Update the AGC gain
158         */
159        AbsPeak = (Abs_32(Left)>Abs_32(Right)) ? Abs_32(Left) : Abs_32(Right);  /* Get the absolute peak */
160        if (AbsPeak > AGC_Target)
161        {
162            /*
163             * The signal is too large so decrease the gain
164             */
165            HighWord = (AGC_Attack * (AGC_Gain >> 16));         /* signed long (AGC_Gain) by unsigned short (AGC_Attack) multiply */
166            LowWord = (AGC_Attack * (AGC_Gain & 0xffff));
167            AGC_Gain = (HighWord + (LowWord >> 16)) << 1;
168        }
169        else
170        {
171            /*
172             * The signal is too small so increase the gain
173             */
174            if (AGC_Gain > AGC_MaxGain)
175            {
176                AGC_Gain -= (AGC_Decay << DECAY_SHIFT);
177            }
178            else
179            {
180                AGC_Gain += (AGC_Decay << DECAY_SHIFT);
181            }
182        }
183
184        /*
185         * Update the gain
186         */
187        Vol_Current += Vol_TC * ((Vol_Target - Vol_Current) >> VOL_TC_SHIFT);
188    }
189
190
191    /*
192     * Update the parameters
193     */
194    pInstance->Volume = Vol_Current;                            /* Actual volume setting */
195    pInstance->AGC_Gain = AGC_Gain;
196
197    return;
198}
199#else
200void AGC_MIX_VOL_2St1Mon_D32_WRA(AGC_MIX_VOL_2St1Mon_FLOAT_t  *pInstance,     /* Instance pointer */
201                                 const LVM_FLOAT            *pStSrc,        /* Stereo source */
202                                 const LVM_FLOAT            *pMonoSrc,      /* Mono source */
203                                 LVM_FLOAT                  *pDst,          /* Stereo destination */
204                                 LVM_UINT16                 NumSamples)     /* Number of samples */
205{
206
207    /*
208     * General variables
209     */
210    LVM_UINT16      i;                                          /* Sample index */
211    LVM_FLOAT       Left;                                       /* Left sample */
212    LVM_FLOAT       Right;                                      /* Right sample */
213    LVM_FLOAT       Mono;                                       /* Mono sample */
214    LVM_FLOAT       AbsPeak;                                    /* Absolute peak signal */
215    LVM_FLOAT       AGC_Mult;                                   /* Short AGC gain */
216    LVM_FLOAT       Vol_Mult;                                   /* Short volume */
217
218
219    /*
220     * Instance control variables
221     */
222    LVM_FLOAT      AGC_Gain      = pInstance->AGC_Gain;         /* Get the current AGC gain */
223    LVM_FLOAT      AGC_MaxGain   = pInstance->AGC_MaxGain;      /* Get maximum AGC gain */
224    LVM_FLOAT      AGC_Attack    = pInstance->AGC_Attack;       /* Attack scaler */
225    LVM_FLOAT      AGC_Decay     = (pInstance->AGC_Decay * (1 << (DECAY_SHIFT)));/* Decay scaler */
226    LVM_FLOAT      AGC_Target    = pInstance->AGC_Target;       /* Get the target level */
227    LVM_FLOAT      Vol_Current   = pInstance->Volume;           /* Actual volume setting */
228    LVM_FLOAT      Vol_Target    = pInstance->Target;           /* Target volume setting */
229    LVM_FLOAT      Vol_TC        = pInstance->VolumeTC;         /* Time constant */
230
231
232    /*
233     * Process on a sample by sample basis
234     */
235    for (i = 0; i < NumSamples; i++)                                  /* For each sample */
236    {
237
238        /*
239         * Get the short scalers
240         */
241        AGC_Mult    = (LVM_FLOAT)(AGC_Gain);              /* Get the short AGC gain */
242        Vol_Mult    = (LVM_FLOAT)(Vol_Current);           /* Get the short volume gain */
243
244
245        /*
246         * Get the input samples
247         */
248        Left  = *pStSrc++;                                      /* Get the left sample */
249        Right = *pStSrc++;                                      /* Get the right sample */
250        Mono  = *pMonoSrc++;                                    /* Get the mono sample */
251
252
253        /*
254         * Apply the AGC gain to the mono input and mix with the stereo signal
255         */
256        Left  += (Mono * AGC_Mult);                               /* Mix in the mono signal */
257        Right += (Mono * AGC_Mult);
258
259        /*
260         * Apply the volume and write to the output stream
261         */
262        Left  = Left  * Vol_Mult;
263        Right = Right * Vol_Mult;
264        *pDst++ = Left;                                         /* Save the results */
265        *pDst++ = Right;
266
267        /*
268         * Update the AGC gain
269         */
270        AbsPeak = Abs_Float(Left) > Abs_Float(Right) ? Abs_Float(Left) : Abs_Float(Right);
271        if (AbsPeak > AGC_Target)
272        {
273            /*
274             * The signal is too large so decrease the gain
275             */
276            AGC_Gain = AGC_Gain * AGC_Attack;
277        }
278        else
279        {
280            /*
281             * The signal is too small so increase the gain
282             */
283            if (AGC_Gain > AGC_MaxGain)
284            {
285                AGC_Gain -= (AGC_Decay);
286            }
287            else
288            {
289                AGC_Gain += (AGC_Decay);
290            }
291        }
292
293        /*
294         * Update the gain
295         */
296        Vol_Current +=  (Vol_Target - Vol_Current) * ((LVM_FLOAT)Vol_TC / VOL_TC_FLOAT);
297    }
298
299
300    /*
301     * Update the parameters
302     */
303    pInstance->Volume = Vol_Current;                            /* Actual volume setting */
304    pInstance->AGC_Gain = AGC_Gain;
305
306    return;
307}
308#endif /*BUILD_FLOAT*/
309