]> git.sesse.net Git - vlc/blob - src/audio_output/filters.c
* Finally fixed the segfault when resampling.
[vlc] / src / audio_output / filters.c
1 /*****************************************************************************
2  * filters.c : audio output filters management
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: filters.c,v 1.7 2002/08/28 22:25:39 massiot Exp $
6  *
7  * Authors: Christophe Massiot <massiot@via.ecp.fr>
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 <stdlib.h>                            /* calloc(), malloc(), free() */
28 #include <string.h>
29
30 #include <vlc/vlc.h>
31
32 #ifdef HAVE_ALLOCA_H
33 #   include <alloca.h> 
34 #endif
35
36 #include "audio_output.h"
37 #include "aout_internal.h"
38
39 /*****************************************************************************
40  * FindFilter: find an audio filter for a specific transformation
41  *****************************************************************************/
42 static aout_filter_t * FindFilter( aout_instance_t * p_aout,
43                              const audio_sample_format_t * p_input_format,
44                              const audio_sample_format_t * p_output_format )
45 {
46     aout_filter_t * p_filter = vlc_object_create( p_aout,
47                                                   sizeof(aout_filter_t) );
48
49     if ( p_filter == NULL ) return NULL;
50     vlc_object_attach( p_filter, p_aout );
51
52     memcpy( &p_filter->input, p_input_format, sizeof(audio_sample_format_t) );
53     memcpy( &p_filter->output, p_output_format,
54             sizeof(audio_sample_format_t) );
55     p_filter->p_module = module_Need( p_filter, "audio filter", NULL );
56     if ( p_filter->p_module == NULL )
57     {
58         vlc_object_detach( p_filter );
59         vlc_object_destroy( p_filter );
60         return NULL;
61     }
62
63     return p_filter;
64 }
65
66 /*****************************************************************************
67  * SplitConversion: split a conversion in two parts 
68  *****************************************************************************
69  * Returns the number of conversions required by the first part - 0 if only
70  * one conversion was asked.
71  * Beware : p_output_format can be modified during this function if the
72  * developer passed SplitConversion( toto, titi, titi, ... ). That is legal.
73  * SplitConversion( toto, titi, toto, ... ) isn't.
74  *****************************************************************************/
75 static int SplitConversion( aout_instance_t * p_aout,
76                              const audio_sample_format_t * p_input_format,
77                              const audio_sample_format_t * p_output_format,
78                              audio_sample_format_t * p_middle_format,
79                              vlc_bool_t b_format_first,
80                              vlc_bool_t b_rate_first )
81 {
82     vlc_bool_t b_format =
83              (p_input_format->i_format != p_output_format->i_format);
84     vlc_bool_t b_rate = (p_input_format->i_rate != p_output_format->i_rate);
85     vlc_bool_t b_channels =
86              (p_input_format->i_channels != p_output_format->i_channels);
87     int i_nb_conversions = b_format + b_rate + b_channels;
88
89     if ( i_nb_conversions <= 1 ) return 0;
90
91     memcpy( p_middle_format, p_output_format, sizeof(audio_sample_format_t) );
92
93     if ( i_nb_conversions == 2 )
94     {
95         if ( !b_format )
96         {
97             if ( b_rate_first )
98             {
99                 p_middle_format->i_channels = p_input_format->i_channels;
100             }
101             else
102             {
103                 p_middle_format->i_rate = p_input_format->i_rate;
104             }
105             return 1;
106         }
107
108         if ( !b_rate )
109         {
110             if ( b_format_first )
111             {
112                 p_middle_format->i_channels = p_input_format->i_channels;
113             }
114             else
115             {
116                 p_middle_format->i_format = p_input_format->i_format;
117             }
118             return 1;
119         }
120
121         /* !b_channels */
122         if ( b_format_first )
123         {
124             p_middle_format->i_rate = p_input_format->i_rate;
125         }
126         else
127         {
128             p_middle_format->i_format = p_input_format->i_format;
129         }
130         return 1;
131     }
132
133     /* i_nb_conversion == 3 */
134     if ( !b_format_first )
135     {
136         p_middle_format->i_format = p_input_format->i_format;
137     }
138     else if ( !b_rate_first )
139     {
140         p_middle_format->i_channels = p_input_format->i_channels;
141     }
142     else
143     {
144         p_middle_format->i_rate = p_input_format->i_rate;
145     }
146
147     return 2;
148 }
149
150 /*****************************************************************************
151  * aout_FiltersCreatePipeline: create a filters pipeline to transform a sample
152  *                             format to another
153  *****************************************************************************
154  * TODO : allow the user to add/remove specific filters
155  *****************************************************************************/
156 int aout_FiltersCreatePipeline( aout_instance_t * p_aout,
157                                 aout_filter_t ** pp_filters,
158                                 int * pi_nb_filters,
159                                 const audio_sample_format_t * p_input_format,
160                                 const audio_sample_format_t * p_output_format )
161 {
162     audio_sample_format_t temp_format;
163     vlc_bool_t b_format_first, b_rate_first;
164
165     if ( AOUT_FMTS_IDENTICAL( p_input_format, p_output_format ) )
166     {
167         msg_Dbg( p_aout, "no need for any filter" );
168         *pi_nb_filters = 0;
169         return 0;
170     }
171
172     msg_Dbg( p_aout, "filter(s) format=%d->%d rate=%d->%d channels=%d->%d",
173              p_input_format->i_format, p_output_format->i_format,
174              p_input_format->i_rate, p_output_format->i_rate,
175              p_input_format->i_channels, p_output_format->i_channels );
176
177     /* Try to find a filter to do the whole conversion. */
178     pp_filters[0] = FindFilter( p_aout, p_input_format, p_output_format );
179     if ( pp_filters[0] != NULL )
180     {
181         msg_Dbg( p_aout, "found a filter for the whole conversion" );
182         *pi_nb_filters = 1;
183         return 0;
184     }
185
186     /* We'll have to split the conversion. We always to the downmixing
187      * before the resampling, and the upmixing after the resampling (to
188      * maximize the resampling efficiency). */
189     b_rate_first = (p_input_format->i_channels < p_output_format->i_channels);
190
191     for ( b_format_first = 1; b_format_first >= 0; b_format_first-- )
192     {
193         int i_nb_conversions = SplitConversion( p_aout, p_input_format,
194                                                p_output_format, &temp_format,
195                                                b_format_first, b_rate_first );
196         if ( !i_nb_conversions )
197         {
198             /* There was only one conversion to do, and we already failed. */
199             msg_Err( p_aout, "couldn't find a filter for the conversion" );
200             return -1;
201         }
202
203         pp_filters[0] = FindFilter( p_aout, p_input_format, &temp_format );
204         if ( pp_filters[0] == NULL && i_nb_conversions == 2 )
205         {
206             /* Try with only one conversion. */
207             SplitConversion( p_aout, p_input_format, &temp_format,
208                              &temp_format, b_format_first, b_rate_first );
209             pp_filters[0] = FindFilter( p_aout, p_input_format,
210                                         &temp_format );
211         }
212         if ( pp_filters[0] == NULL )
213         {
214             /* Retry with b_format_first = 0. */
215             continue;
216         }
217
218         /* We have the first stage of the conversion. Find a filter for
219          * the rest. */
220         pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output,
221                                     p_output_format );
222         if ( pp_filters[1] == NULL )
223         {
224             /* Try to split the conversion. */
225             i_nb_conversions = SplitConversion( p_aout,
226                                     &pp_filters[0]->output,
227                                     p_output_format, &temp_format,
228                                     b_format_first, b_rate_first );
229             if ( !i_nb_conversions )
230             {
231                 vlc_object_detach( pp_filters[0] );
232                 vlc_object_destroy( pp_filters[0] );
233                 continue;
234             }
235             pp_filters[1] = FindFilter( p_aout, &pp_filters[0]->output,
236                                         &temp_format );
237             pp_filters[2] = FindFilter( p_aout, &temp_format,
238                                         p_output_format );
239
240             if ( pp_filters[1] == NULL || pp_filters[2] == NULL )
241             {
242                 vlc_object_detach( pp_filters[0] );
243                 vlc_object_destroy( pp_filters[0] );
244                 if ( pp_filters[1] != NULL )
245                 {
246                     vlc_object_detach( pp_filters[1] );
247                     vlc_object_destroy( pp_filters[1] );
248                 }
249                 if ( pp_filters[2] != NULL )
250                 {
251                     vlc_object_detach( pp_filters[2] );
252                     vlc_object_destroy( pp_filters[2] );
253                 }
254                 continue;
255             }
256             *pi_nb_filters = 3;
257         }
258         else
259         {
260             *pi_nb_filters = 2;
261         }
262
263         /* We have enough filters. */
264         msg_Dbg( p_aout, "found %d filters for the whole conversion",
265                  *pi_nb_filters );
266         return 0;
267     }
268
269     msg_Err( p_aout, "couldn't find filters for the conversion" );
270     return -1;
271 }
272
273 /*****************************************************************************
274  * aout_FiltersDestroyPipeline: deallocate a filters pipeline
275  *****************************************************************************/
276 void aout_FiltersDestroyPipeline( aout_instance_t * p_aout,
277                                   aout_filter_t ** pp_filters,
278                                   int i_nb_filters )
279 {
280     int i;
281
282     for ( i = 0; i < i_nb_filters; i++ )
283     {
284         module_Unneed( pp_filters[i], pp_filters[i]->p_module );
285         vlc_object_detach( pp_filters[i] );
286         vlc_object_destroy( pp_filters[i] );
287     }
288 }
289
290 /*****************************************************************************
291  * aout_FiltersHintBuffers: fill in aout_alloc_t structures to optimize
292  *                          buffer allocations
293  *****************************************************************************/
294 void aout_FiltersHintBuffers( aout_instance_t * p_aout,
295                               aout_filter_t ** pp_filters,
296                               int i_nb_filters, aout_alloc_t * p_first_alloc )
297 {
298     int i;
299
300     for ( i = i_nb_filters - 1; i >= 0; i-- )
301     {
302         aout_filter_t * p_filter = pp_filters[i];
303
304         int i_output_size = p_filter->output.i_bytes_per_frame
305                              * p_filter->output.i_rate
306                              / p_filter->output.i_frame_length;
307         int i_input_size = p_filter->input.i_bytes_per_frame
308                              * p_filter->input.i_rate
309                              / p_filter->input.i_frame_length;
310
311         p_first_alloc->i_bytes_per_sec = __MAX( p_first_alloc->i_bytes_per_sec,
312                                                 i_output_size );
313
314         if ( p_filter->b_in_place )
315         {
316             p_first_alloc->i_bytes_per_sec = __MAX(
317                                          p_first_alloc->i_bytes_per_sec,
318                                          i_input_size );
319             p_filter->output_alloc.i_alloc_type = AOUT_ALLOC_NONE;
320         }
321         else
322         {
323             /* We're gonna need a buffer allocation. */
324             memcpy( &p_filter->output_alloc, p_first_alloc,
325                     sizeof(aout_alloc_t) );
326             p_first_alloc->i_alloc_type = AOUT_ALLOC_STACK;
327             p_first_alloc->i_bytes_per_sec = i_input_size;
328         }
329     }
330 }
331
332 /*****************************************************************************
333  * aout_FiltersPlay: play a buffer
334  *****************************************************************************/
335 void aout_FiltersPlay( aout_instance_t * p_aout,
336                        aout_filter_t ** pp_filters,
337                        int i_nb_filters, aout_buffer_t ** pp_input_buffer )
338 {
339     int i;
340
341     for ( i = 0; i < i_nb_filters; i++ )
342     {
343         aout_filter_t * p_filter = pp_filters[i];
344         aout_buffer_t * p_output_buffer;
345
346         aout_BufferAlloc( &p_filter->output_alloc,
347                           (mtime_t)(*pp_input_buffer)->i_nb_samples * 1000000
348                             / p_filter->input.i_rate, *pp_input_buffer,
349                           p_output_buffer );
350         if ( p_output_buffer == NULL )
351         {
352             msg_Err( p_aout, "out of memory" );
353             return;
354         }
355         /* Please note that p_output_buffer->i_nb_samples & i_nb_bytes
356          * shall be set by the filter plug-in. */
357
358         p_filter->pf_do_work( p_aout, p_filter, *pp_input_buffer,
359                               p_output_buffer );
360
361         if ( !p_filter->b_in_place )
362         {
363             aout_BufferFree( *pp_input_buffer );
364             *pp_input_buffer = p_output_buffer;
365         }
366     }
367 }
368