summaryrefslogtreecommitdiff
path: root/apps/codecs/libcook/cook_fixpoint.h
blob: 30e5a3eee27439bdb44f1c73f628cd88cb775094 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
 * COOK compatible decoder, fixed point implementation.
 * Copyright (c) 2007 Ian Braithwaite
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 */

/**
 * @file cook_fixpoint.h
 *
 * Cook AKA RealAudio G2 fixed point functions.
 *
 * Fixed point values are represented as 32 bit signed integers,
 * which can be added and subtracted directly in C (without checks for
 * overflow/saturation.
 * Two multiplication routines are provided:
 * 1) Multiplication by powers of two (2^-31 .. 2^31), implemented
 *    with C's bit shift operations.
 * 2) Multiplication by 16 bit fractions (0 <= x < 1), implemented
 *    in C using two 32 bit integer multiplications.
 */

#ifdef ROCKBOX
/* get definitions of MULT31, MULT31_SHIFT15, CLIP_TO_15, vect_add, from codelib */
#include "asm_arm.h"
#include "asm_mcf5249.h"
#include "codeclib_misc.h"
#include "codeclib.h"
#endif

/* cplscales was moved from cookdata_fixpoint.h since only   *
 * cook_fixpoint.h should see/use it.                        */
static const FIXPU* cplscales[5] = {
    cplscale2, cplscale3, cplscale4, cplscale5, cplscale6
};

/**
 * Fixed point multiply by power of two.
 *
 * @param x                     fix point value
 * @param i                     integer power-of-two, -31..+31
 */
static inline FIXP fixp_pow2(FIXP x, int i)
{
  if (i < 0)
    return (x >> -i) + ((x >> (-i-1)) & 1);
  else
    return x << i;              /* no check for overflow */
}

static inline FIXP fixp_pow2_neg(FIXP x, int i)
{
  return (x >> i) + ((x >> (i-1)) & 1);
}

/**
 * Fixed point multiply by fraction.
 *
 * @param a                     fix point value
 * @param b                     fix point fraction, 0 <= b < 1
 */
#ifdef ROCKBOX
#define fixp_mult_su(x,y) (MULT31_SHIFT15(x,y))
#else
static inline FIXP fixp_mult_su(FIXP a, FIXPU b)
{
    int32_t hb = (a >> 16) * b; 	 
    uint32_t lb = (a & 0xffff) * b; 	 

    return hb + (lb >> 16) + ((lb & 0x8000) >> 15); 	 
}
#endif

/* Faster version of the above using 32x32=64 bit multiply */
#ifdef ROCKBOX
#define fixmul31(x,y) (MULT31(x,y))
#else 	 
static inline int32_t fixmul31(int32_t x, int32_t y) 	 
{ 	 
    int64_t temp; 	 

    temp = x; 	 
    temp *= y; 	 

    temp >>= 31;        //16+31-16 = 31 bits 	 
    
    return (int32_t)temp; 	 
} 	 
#endif

/**
 * Clips a signed integer value into the amin-amax range.
 * @param a value to clip
 * @param amin minimum value of the clip range
 * @param amax maximum value of the clip range
 * @return clipped value
 */
static inline int av_clip(int a, int amin, int amax)
{
    if      (a < amin) return amin;
    else if (a > amax) return amax;
    else               return a;
}

/**
 * The real requantization of the mltcoefs
 *
 * @param q                     pointer to the COOKContext
 * @param index                 index
 * @param quant_index           quantisation index for this band
 * @param subband_coef_index    array of indexes to quant_centroid_tab
 * @param subband_coef_sign     use random noise instead of predetermined value
 * @param mlt_ptr               pointer to the mlt coefficients
 */
static void scalar_dequant_math(COOKContext *q, int index,
                                int quant_index, int* subband_coef_index,
                                int* subband_coef_sign, REAL_T *mlt_p)
{
    /* Num. half bits to right shift */
    const int s = (33 - quant_index + av_log2(q->samples_per_channel)) >> 1;
    const FIXP *table = quant_tables[s & 1][index];
    FIXP f;
    int i;


    if(s >= 32)
        memset(mlt_p, 0, sizeof(REAL_T)*SUBBAND_SIZE);
    else 
    {
        for(i=0 ; i<SUBBAND_SIZE ; i++) {
            f = table[subband_coef_index[i]];
            /* noise coding if subband_coef_index[i] == 0 */
            if (((subband_coef_index[i] == 0) && cook_random(q)) ||
                ((subband_coef_index[i] != 0) && subband_coef_sign[i]))
                f = -f;

            *mlt_p++ = fixp_pow2_neg(f, s);
        }
    }
}

/**
 * The modulated lapped transform, this takes transform coefficients
 * and transforms them into timedomain samples.
 * A window step is also included.
 *
 * @param q                 pointer to the COOKContext
 * @param inbuffer          pointer to the mltcoefficients
 * @param outbuffer         pointer to the timedomain buffer
 * @param mlt_tmp           pointer to temporary storage space
 */
#include "../lib/mdct_lookup.h"

static inline void imlt_math(COOKContext *q, FIXP *in)
{
    const int n = q->samples_per_channel;
    const int step = 2 << (10 - av_log2(n));
    int i = 0, j = 0;

    ff_imdct_calc(q->mdct_nbits, q->mono_mdct_output, in);

    do {
        FIXP tmp = q->mono_mdct_output[i];
        
        q->mono_mdct_output[i] =
          fixmul31(-q->mono_mdct_output[n + i], (sincos_lookup0[j]));
          
        q->mono_mdct_output[n + i] = fixmul31(tmp, (sincos_lookup0[j+1]) );
            
        j += step;
        
    } while (++i < n/2);

    do {
        FIXP tmp = q->mono_mdct_output[i];
        
        j -= step;
        q->mono_mdct_output[i] =
          fixmul31(-q->mono_mdct_output[n + i], (sincos_lookup0[j+1]) );
        q->mono_mdct_output[n + i] = fixmul31(tmp, (sincos_lookup0[j]) );
    } while (++i < n);
}

/**
 * Perform buffer overlapping.
 *
 * @param q                 pointer to the COOKContext
 * @param gain              gain correction to apply first to output buffer
 * @param buffer            data to overlap
 */
static inline void overlap_math(COOKContext *q, int gain, FIXP buffer[])
{
    int i;
#ifdef ROCKBOX
    if(LIKELY(gain == 0))
    {
        vect_add(q->mono_mdct_output, buffer, q->samples_per_channel);
        
    } else if (gain > 0){
        for(i=0 ; i<q->samples_per_channel ; i++) {
            q->mono_mdct_output[i] = (q->mono_mdct_output[i]<< gain) + buffer[i];        }          
        
    } else {
        for(i=0 ; i<q->samples_per_channel ; i++) {
            q->mono_mdct_output[i] =
              (q->mono_mdct_output[i] >> -gain) + ((q->mono_mdct_output[i] >> (-gain-1)) & 1)+ buffer[i];
        }
    }
#else
    for(i=0 ; i<q->samples_per_channel ; i++) {
        q->mono_mdct_output[i] =
          fixp_pow2(q->mono_mdct_output[i], gain) + buffer[i];
    }
#endif
}


/**
 * the actual requantization of the timedomain samples
 *
 * @param q                 pointer to the COOKContext
 * @param buffer            pointer to the timedomain buffer
 * @param gain_index        index for the block multiplier
 * @param gain_index_next   index for the next block multiplier
 */
static inline void
interpolate_math(COOKContext *q, register FIXP* buffer,
                 int gain_index, int gain_index_next)
{
    int i;
    int gain_size_factor = q->samples_per_channel / 8;

    if(gain_index == gain_index_next){              //static gain
        for(i = 0; i < gain_size_factor; i++) {
            buffer[i] = fixp_pow2(buffer[i], gain_index);
        }
    } else {                                        //smooth gain
        int step = (gain_index_next - gain_index)
                   << (7 - av_log2(gain_size_factor));
        int x = 0;
        register FIXP* bufferend = buffer+gain_size_factor;
        while(buffer < bufferend )
        {
            *buffer = fixp_pow2(
                          fixp_mult_su(*buffer, pow128_tab[x]),
                          gain_index+1);
            buffer++;

            x += step;
            gain_index += ( (x + 128) >> 7 ) - 1;
            x = ( (x + 128) & 127 );
        }
    }
}


/**
 * Decoupling calculation for joint stereo coefficients.
 *
 * @param x                 mono coefficient
 * @param table             number of decoupling table
 * @param i                 table index
 */
static inline FIXP cplscale_math(FIXP x, int table, int i)
{
  return fixp_mult_su(x, cplscales[table-2][i]);
}


/**
 * Final converion from floating point values to
 * signed, 16 bit sound samples. Round and clip.
 *
 * @param q                 pointer to the COOKContext
 * @param out               pointer to the output buffer
 * @param chan              0: left or single channel, 1: right channel
 */
static inline void output_math(COOKContext *q, register int16_t *out, int chan)
{
#ifdef ROCKBOX
    register REAL_T * mono_output_ptr = q->mono_mdct_output;
    register REAL_T * mono_output_end = mono_output_ptr + q->samples_per_channel;
    out += chan;
    const int STEP = q->nb_channels;
    while( mono_output_ptr < mono_output_end )
    {
      *out = CLIP_TO_15(fixp_pow2_neg(*mono_output_ptr++, 11));
      out += STEP;
    }
#else
    int j;
    for (j = 0; j < q->samples_per_channel; j++) {
        out[chan + q->nb_channels * j] =
        av_clip(fixp_pow2(q->mono_mdct_output[j], -11), -32768, 32767);
    }
#endif
}