]> git.sesse.net Git - vlc/blob - modules/audio_filter/converter/fixed32tos16.c
* Fixed filters which couldn't work with more than 2 channels ;
[vlc] / modules / audio_filter / converter / fixed32tos16.c
1 /*****************************************************************************
2  * fixed32tos16.c : converter from fixed32 to signed 16 bits integer
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: fixed32tos16.c,v 1.7 2002/10/15 23:10:54 massiot Exp $
6  *
7  * Authors: Jean-Paul Saman <jpsaman@wxs.nl>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <errno.h>
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <vlc/vlc.h>
32 #include "audio_output.h"
33 #include "aout_internal.h"
34
35 /*****************************************************************************
36  * Local prototypes
37  *****************************************************************************/
38 static int  Create    ( vlc_object_t * );
39
40 static void DoWork    ( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
41                         aout_buffer_t * );
42
43 /*****************************************************************************
44  * Module descriptor
45  *****************************************************************************/
46 vlc_module_begin();
47     set_description( _("audio filter for fixed32->s16 conversion") );
48     set_capability( "audio filter", 10 );
49     set_callbacks( Create, NULL );
50 vlc_module_end();
51
52 /*****************************************************************************
53  * Create: allocate trivial mixer
54  *****************************************************************************
55  * This function allocates and initializes a Crop vout method.
56  *****************************************************************************/
57 static int Create( vlc_object_t *p_this )
58 {
59     aout_filter_t * p_filter = (aout_filter_t *)p_this;
60
61     if ( p_filter->input.i_format != VLC_FOURCC('f','i','3','2')
62           || p_filter->output.i_format != AOUT_FMT_S16_NE )
63     {
64         return -1;
65     }
66
67     if ( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
68     {
69         return -1;
70     }
71
72     p_filter->pf_do_work = DoWork;
73     p_filter->b_in_place = 1;
74
75     return 0;
76 }
77
78 /*****************************************************************************
79  * support routines borrowed from mpg321 (file: mad.c), which is distributed
80  * under GPL license
81  *
82  * mpg321 was written by Joe Drew <drew@debian.org>, and based upon 'plaympeg'
83  * from the smpeg sources, which was written by various people from Loki Software
84  * (http://www.lokigames.com).
85  *
86  * It also incorporates some source from mad, written by Robert Leslie
87  *****************************************************************************/
88
89 /* The following two routines and data structure are from the ever-brilliant
90      Rob Leslie.
91 */
92
93 #define VLC_F_FRACBITS  28
94
95 # if VLC_F_FRACBITS == 28
96 #  define VLC_F(x)              ((vlc_fixed_t) (x##L))
97 # endif
98
99 # define VLC_F_ONE              VLC_F(0x10000000)
100
101 struct audio_dither {
102     vlc_fixed_t error[3];
103     vlc_fixed_t random;
104 };
105
106 /********************************************************************
107  * NAME:                prng()
108  * DESCRIPTION: 32-bit pseudo-random number generator
109  ********************************************************************/
110 static inline unsigned long prng(unsigned long state)
111 {
112     return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
113 }
114
115 /********************************************************************
116  * NAME:        mpg321_s24_to_s16_pcm()
117  * DESCRIPTION: generic linear sample quantize and dither routine
118  ********************************************************************/
119 static inline s16 mpg321_s24_to_s16_pcm(unsigned int bits, vlc_fixed_t sample,
120                                     struct audio_dither *dither)
121 {
122     unsigned int scalebits;
123     vlc_fixed_t output, mask, random;
124
125     enum {
126         MIN = -VLC_F_ONE,
127         MAX = VLC_F_ONE - 1
128     };
129
130     /* noise shape */
131     sample += dither->error[0] - dither->error[1] + dither->error[2];
132
133     dither->error[2] = dither->error[1];
134     dither->error[1] = dither->error[0] / 2;
135
136     /* bias */
137     output = sample + (1L << (VLC_F_FRACBITS + 1 - bits - 1));
138
139     scalebits = VLC_F_FRACBITS + 1 - bits;
140     mask = (1L << scalebits) - 1;
141
142     /* dither */
143     random    = prng(dither->random);
144     output += (random & mask) - (dither->random & mask);
145
146     dither->random = random;
147
148     /* clip */
149     if (output > MAX) {
150         output = MAX;
151
152         if (sample > MAX)
153             sample = MAX;
154     }
155     else if (output < MIN) {
156         output = MIN;
157
158         if (sample < MIN)
159             sample = MIN;
160     }
161
162     /* quantize */
163     output &= ~mask;
164
165     /* error feedback */
166     dither->error[0] = sample - output;
167
168     /* scale */
169     return output >> scalebits;
170 }
171
172 /*****************************************************************************
173  * s24_to_s16_pcm: Scale a 24 bit pcm sample to a 16 bit pcm sample.
174  *****************************************************************************/
175 static inline s16 s24_to_s16_pcm(vlc_fixed_t sample)
176 {
177   /* round */
178   sample += (1L << (VLC_F_FRACBITS - 16));
179
180   /* clip */
181   if (sample >= VLC_F_ONE)
182     sample = VLC_F_ONE - 1;
183   else if (sample < -VLC_F_ONE)
184     sample = -VLC_F_ONE;
185
186   /* quantize */
187   return (sample >> (VLC_F_FRACBITS + 1 - 16));
188 }
189
190 /*****************************************************************************
191  * DoWork: convert a buffer
192  *****************************************************************************/
193 static void DoWork( aout_instance_t * p_aout, aout_filter_t * p_filter,
194                     aout_buffer_t * p_in_buf, aout_buffer_t * p_out_buf )
195 {
196     int i;
197     vlc_fixed_t * p_in = (vlc_fixed_t *)p_in_buf->p_buffer;
198     s16 * p_out = (s16 *)p_out_buf->p_buffer;
199     s16 sample;
200 //    static struct audio_dither dither;
201
202     for ( i = p_in_buf->i_nb_samples
203                * aout_FormatNbChannels( &p_filter->input ) ; i-- ; )
204     {
205         /* Accurate scaling */
206 //        p_out = mpg321_s24_to_s16_pcm(16, *p_in++, &dither);
207         /* Fast Scaling */
208         sample = s24_to_s16_pcm(*p_in++);
209
210 #ifndef WORDS_BIGENDIAN
211         *p_out++ = (s16) (sample >> 0);
212         *p_out++ = (s16) (sample >> 8);
213 #else
214         *p_out++ = (s16) (sample >> 8);
215         *p_out++ = (s16) (sample >> 0);
216 #endif
217 //        p_in++; p_out++;
218     }
219     p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
220     p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes / 2;
221 }