]> git.sesse.net Git - vlc/blob - modules/audio_filter/resampler/linear.c
a3c7cb3a6b4d9404763df05dff77a9b009a3b901
[vlc] / modules / audio_filter / resampler / linear.c
1 /*****************************************************************************
2  * linear.c : linear interpolation resampler
3  *****************************************************************************
4  * Copyright (C) 2002, 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *          Sigmund Augdal Helberg <dnumgis@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_aout.h>
36 #include <vlc_filter.h>
37 #include <vlc_block.h>
38 #include <vlc_cpu.h>
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int  OpenFilter ( vlc_object_t * );
44 static void CloseFilter( vlc_object_t * );
45 static block_t *Resample( filter_t *, block_t * );
46
47 #if HAVE_FPU
48 typedef float sample_t;
49 # define VLC_CODEC_NATIVE VLC_CODEC_FL32
50 #else
51 typedef int32_t sample_t;
52 # define VLC_CODEC_NATIVE VLC_CODEC_FI32
53 #endif
54
55 /*****************************************************************************
56  * Local structures
57  *****************************************************************************/
58 struct filter_sys_t
59 {
60     sample_t *p_prev_sample;      /* this filter introduces a 1 sample delay */
61
62     unsigned int i_remainder;                /* remainder of previous sample */
63
64     date_t       end_date;
65 };
66
67 /*****************************************************************************
68  * Module descriptor
69  *****************************************************************************/
70 vlc_module_begin ()
71     set_description( N_("Audio filter for linear interpolation resampling") )
72     set_category( CAT_AUDIO )
73     set_subcategory( SUBCAT_AUDIO_MISC )
74     set_capability( "audio filter", 5 )
75     set_callbacks( OpenFilter, CloseFilter )
76 vlc_module_end ()
77
78 /*****************************************************************************
79  * Resample: convert a buffer
80  *****************************************************************************/
81 static block_t *Resample( filter_t *p_filter, block_t *p_in_buf )
82 {
83     if( !p_in_buf || !p_in_buf->i_nb_samples )
84     {
85         if( p_in_buf )
86             block_Release( p_in_buf );
87         return NULL;
88     }
89
90     filter_sys_t *p_sys = p_filter->p_sys;
91     unsigned i_nb_channels = p_filter->fmt_in.audio.i_channels;
92     sample_t *p_prev_sample = p_sys->p_prev_sample;
93
94     /* Check if we really need to run the resampler */
95     if( p_filter->fmt_out.audio.i_rate == p_filter->fmt_in.audio.i_rate )
96     {
97         if( !(p_in_buf->i_flags & BLOCK_FLAG_DISCONTINUITY) )
98         {
99             p_in_buf = block_Realloc( p_in_buf,
100                                       sizeof(sample_t) * i_nb_channels,
101                                       p_in_buf->i_buffer );
102             if( !p_in_buf )
103                 return NULL;
104
105             memcpy( p_in_buf->p_buffer, p_prev_sample,
106                     i_nb_channels * sizeof(sample_t) );
107         }
108         return p_in_buf;
109     }
110
111     unsigned i_bytes_per_frame = p_filter->fmt_out.audio.i_channels *
112                                  p_filter->fmt_out.audio.i_bitspersample / 8;
113
114     size_t i_out_size = i_bytes_per_frame * (1 + (p_in_buf->i_nb_samples *
115               p_filter->fmt_out.audio.i_rate / p_filter->fmt_in.audio.i_rate));
116     block_t *p_out_buf = filter_NewAudioBuffer( p_filter, i_out_size );
117     if( !p_out_buf )
118         goto out;
119
120     sample_t *p_out = (sample_t *)p_out_buf->p_buffer;
121
122     unsigned i_in_nb = p_in_buf->i_nb_samples;
123     unsigned i_out = 0;
124     const sample_t *p_in = (sample_t *)p_in_buf->p_buffer;
125
126     /* Take care of the previous input sample (if any) */
127     if( p_in_buf->i_flags & BLOCK_FLAG_DISCONTINUITY )
128     {
129         p_out_buf->i_flags |= BLOCK_FLAG_DISCONTINUITY;
130         p_sys->i_remainder = 0;
131         date_Init( &p_sys->end_date, p_filter->fmt_out.audio.i_rate, 1 );
132     }
133     else
134     {
135         while( p_sys->i_remainder < p_filter->fmt_out.audio.i_rate )
136         {
137             for( unsigned i = 0; i < i_nb_channels ; i++ )
138             {
139                 p_out[i] = p_prev_sample[i];
140 #if HAVE_FPU
141                 p_out[i] += (p_in[i] - p_prev_sample[i])
142 #else
143                 p_out[i] += (int64_t)(p_in[i] - p_prev_sample[i])
144 #endif
145                     * p_sys->i_remainder / p_filter->fmt_out.audio.i_rate;
146             }
147             p_out += i_nb_channels;
148             i_out++;
149
150             p_sys->i_remainder += p_filter->fmt_in.audio.i_rate;
151         }
152         p_sys->i_remainder -= p_filter->fmt_out.audio.i_rate;
153     }
154
155     /* Take care of the current input samples (minus last one) */
156     for( unsigned i_in = 0; i_in < i_in_nb - 1; i_in++ )
157     {
158         while( p_sys->i_remainder < p_filter->fmt_out.audio.i_rate )
159         {
160             for( unsigned i = 0; i < i_nb_channels ; i++ )
161             {
162                 p_out[i] = p_in[i];
163 #if HAVE_FPU
164                 p_out[i] += (p_in[i + i_nb_channels] - p_in[i])
165 #else
166                 p_out[i] += (int64_t)(p_in[i + i_nb_channels] - p_in[i])
167 #endif
168                     * p_sys->i_remainder / p_filter->fmt_out.audio.i_rate;
169             }
170             p_out += i_nb_channels;
171             i_out++;
172
173             p_sys->i_remainder += p_filter->fmt_in.audio.i_rate;
174         }
175
176         p_in += i_nb_channels;
177         p_sys->i_remainder -= p_filter->fmt_out.audio.i_rate;
178     }
179
180     /* Backup the last input sample for next time */
181     memcpy( p_prev_sample, p_in, i_nb_channels * sizeof(sample_t) );
182
183     p_out_buf->i_nb_samples = i_out;
184     p_out_buf->i_pts = p_in_buf->i_pts;
185
186     if( p_in_buf->i_pts !=
187         date_Get( &p_sys->end_date ) )
188     {
189         date_Set( &p_sys->end_date, p_in_buf->i_pts );
190     }
191
192     p_out_buf->i_length = date_Increment( &p_sys->end_date,
193                                   p_out_buf->i_nb_samples ) - p_out_buf->i_pts;
194
195     p_out_buf->i_buffer = p_out_buf->i_nb_samples *
196         i_nb_channels * sizeof(sample_t);
197 out:
198     block_Release( p_in_buf );
199     return p_out_buf;
200 }
201
202 /*****************************************************************************
203  * OpenFilter:
204  *****************************************************************************/
205 static int OpenFilter( vlc_object_t *p_this )
206 {
207     filter_t *p_filter = (filter_t *)p_this;
208     filter_sys_t *p_sys;
209     int i_out_rate  = p_filter->fmt_out.audio.i_rate;
210
211     if( p_filter->fmt_in.audio.i_rate == p_filter->fmt_out.audio.i_rate ||
212         p_filter->fmt_in.i_codec != VLC_CODEC_NATIVE )
213     {
214         return VLC_EGENERIC;
215     }
216  
217     /* Allocate the memory needed to store the module's structure */
218     p_filter->p_sys = p_sys = malloc( sizeof(struct filter_sys_t) );
219     if( p_sys == NULL )
220         return VLC_ENOMEM;
221
222     p_sys->p_prev_sample = malloc(
223         p_filter->fmt_in.audio.i_channels * sizeof(sample_t) );
224     if( p_sys->p_prev_sample == NULL )
225     {
226         free( p_sys );
227         return VLC_ENOMEM;
228     }
229     date_Init( &p_sys->end_date, p_filter->fmt_in.audio.i_rate, 1 );
230     p_sys->i_remainder = 0;
231
232     p_filter->pf_audio_filter = Resample;
233
234     msg_Dbg( p_this, "%4.4s/%iKHz/%i->%4.4s/%iKHz/%i",
235              (char *)&p_filter->fmt_in.i_codec,
236              p_filter->fmt_in.audio.i_rate,
237              p_filter->fmt_in.audio.i_channels,
238              (char *)&p_filter->fmt_out.i_codec,
239              p_filter->fmt_out.audio.i_rate,
240              p_filter->fmt_out.audio.i_channels);
241
242     p_filter->fmt_out = p_filter->fmt_in;
243     p_filter->fmt_out.audio.i_rate = i_out_rate;
244
245     return 0;
246 }
247
248 /*****************************************************************************
249  * CloseFilter : deallocate data structures
250  *****************************************************************************/
251 static void CloseFilter( vlc_object_t *p_this )
252 {
253     filter_t *p_filter = (filter_t *)p_this;
254     free( p_filter->p_sys->p_prev_sample );
255     free( p_filter->p_sys );
256 }