]> git.sesse.net Git - vlc/blob - src/audio_output/mixer.c
Pass mixer multiplier as argument
[vlc] / src / audio_output / mixer.c
1 /*****************************************************************************
2  * mixer.c : audio output mixing operations
3  *****************************************************************************
4  * Copyright (C) 2002-2004 the VideoLAN team
5  * $Id$
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30 #include <assert.h>
31
32 #include <stddef.h>
33 #include <vlc_common.h>
34 #include <libvlc.h>
35 #include <vlc_modules.h>
36
37 #include <vlc_aout.h>
38 #include "aout_internal.h"
39 /*****************************************************************************
40  * aout_MixerNew: prepare a mixer plug-in
41  *****************************************************************************
42  * Please note that you must hold the mixer lock.
43  *****************************************************************************/
44 int aout_MixerNew( aout_instance_t * p_aout )
45 {
46     assert( !p_aout->p_mixer );
47     vlc_assert_locked( &p_aout->input_fifos_lock );
48
49     aout_mixer_t *p_mixer = vlc_object_create( p_aout, sizeof(*p_mixer) );
50     if( !p_mixer )
51         return VLC_EGENERIC;
52
53     p_mixer->fmt = p_aout->mixer_format;
54     p_mixer->input = &p_aout->pp_inputs[0]->mixer;
55     p_mixer->mix = NULL;
56     p_mixer->sys = NULL;
57
58     p_mixer->module = module_need( p_mixer, "audio mixer", NULL, false );
59     if( !p_mixer->module )
60     {
61         msg_Err( p_aout, "no suitable audio mixer" );
62         vlc_object_release( p_mixer );
63         return VLC_EGENERIC;
64     }
65
66     /* */
67     p_aout->p_mixer = p_mixer;
68     return VLC_SUCCESS;
69 }
70
71 /*****************************************************************************
72  * aout_MixerDelete: delete the mixer
73  *****************************************************************************
74  * Please note that you must hold the mixer lock.
75  *****************************************************************************/
76 void aout_MixerDelete( aout_instance_t * p_aout )
77 {
78     if( !p_aout->p_mixer )
79         return;
80
81     module_unneed( p_aout->p_mixer, p_aout->p_mixer->module );
82
83     vlc_object_release( p_aout->p_mixer );
84
85     /* */
86     p_aout->p_mixer = NULL;
87 }
88
89 /*****************************************************************************
90  * MixBuffer: try to prepare one output buffer
91  *****************************************************************************
92  * Please note that you must hold the mixer lock.
93  *****************************************************************************/
94 static int MixBuffer( aout_instance_t * p_aout, float volume )
95 {
96     int             i, i_first_input = 0;
97     mtime_t start_date, end_date;
98     date_t  exact_start_date;
99
100     if( !p_aout->p_mixer )
101     {
102         /* Free all incoming buffers. */
103         aout_lock_input_fifos( p_aout );
104         for ( i = 0; i < p_aout->i_nb_inputs; i++ )
105         {
106             aout_input_t * p_input = p_aout->pp_inputs[i];
107             aout_buffer_t * p_buffer = p_input->mixer.fifo.p_first;
108             if ( p_input->b_error ) continue;
109             while ( p_buffer != NULL )
110             {
111                 aout_buffer_t * p_next = p_buffer->p_next;
112                 aout_BufferFree( p_buffer );
113                 p_buffer = p_next;
114             }
115         }
116         aout_unlock_input_fifos( p_aout );
117         return -1;
118     }
119
120
121     aout_lock_input_fifos( p_aout );
122     aout_lock_output_fifo( p_aout );
123
124     /* Retrieve the date of the next buffer. */
125     exact_start_date = p_aout->output.fifo.end_date;
126     start_date = date_Get( &exact_start_date );
127
128     if ( start_date != 0 && start_date < mdate() )
129     {
130         /* The output is _very_ late. This can only happen if the user
131          * pauses the stream (or if the decoder is buggy, which cannot
132          * happen :). */
133         msg_Warn( p_aout, "output PTS is out of range (%"PRId64"), clearing out",
134                   mdate() - start_date );
135         aout_FifoSet( p_aout, &p_aout->output.fifo, 0 );
136         date_Set( &exact_start_date, 0 );
137         start_date = 0;
138     }
139
140     aout_unlock_output_fifo( p_aout );
141
142     /* See if we have enough data to prepare a new buffer for the audio
143      * output. First : start date. */
144     if ( !start_date )
145     {
146         /* Find the latest start date available. */
147         for ( i = 0; i < p_aout->i_nb_inputs; i++ )
148         {
149             aout_input_t * p_input = p_aout->pp_inputs[i];
150             aout_fifo_t * p_fifo = &p_input->mixer.fifo;
151             aout_buffer_t * p_buffer;
152
153             if ( p_input->b_error || p_input->b_paused )
154                 continue;
155
156             p_buffer = p_fifo->p_first;
157             while ( p_buffer != NULL && p_buffer->i_pts < mdate() )
158             {
159                 msg_Warn( p_aout, "input PTS is out of range (%"PRId64"), "
160                           "trashing", mdate() - p_buffer->i_pts );
161                 p_buffer = aout_FifoPop( p_aout, p_fifo );
162                 aout_BufferFree( p_buffer );
163                 p_buffer = p_fifo->p_first;
164                 p_input->mixer.begin = NULL;
165             }
166
167             if ( p_buffer == NULL )
168             {
169                 break;
170             }
171
172             if ( !start_date || start_date < p_buffer->i_pts )
173             {
174                 date_Set( &exact_start_date, p_buffer->i_pts );
175                 start_date = p_buffer->i_pts;
176             }
177         }
178
179         if ( i < p_aout->i_nb_inputs )
180         {
181             /* Interrupted before the end... We can't run. */
182             aout_unlock_input_fifos( p_aout );
183             return -1;
184         }
185     }
186     date_Increment( &exact_start_date, p_aout->output.i_nb_samples );
187     end_date = date_Get( &exact_start_date );
188
189     /* Check that start_date and end_date are available for all input
190      * streams. */
191     for ( i = 0; i < p_aout->i_nb_inputs; i++ )
192     {
193         aout_input_t * p_input = p_aout->pp_inputs[i];
194         aout_fifo_t * p_fifo = &p_input->mixer.fifo;
195         aout_buffer_t * p_buffer;
196         mtime_t prev_date;
197         bool b_drop_buffers;
198
199         p_input->mixer.is_invalid = p_input->b_error || p_input->b_paused;
200         if ( p_input->mixer.is_invalid )
201         {
202             if ( i_first_input == i ) i_first_input++;
203             continue;
204         }
205
206         p_buffer = p_fifo->p_first;
207         if ( p_buffer == NULL )
208         {
209             break;
210         }
211
212         /* Check for the continuity of start_date */
213         while ( p_buffer != NULL
214              && p_buffer->i_pts + p_buffer->i_length < start_date - 1 )
215         {
216             /* We authorize a +-1 because rounding errors get compensated
217              * regularly. */
218             aout_buffer_t * p_next = p_buffer->p_next;
219             msg_Warn( p_aout, "the mixer got a packet in the past (%"PRId64")",
220                       start_date - (p_buffer->i_pts + p_buffer->i_length) );
221             aout_BufferFree( p_buffer );
222             p_fifo->p_first = p_buffer = p_next;
223             p_input->mixer.begin = NULL;
224         }
225         if ( p_buffer == NULL )
226         {
227             p_fifo->pp_last = &p_fifo->p_first;
228             break;
229         }
230
231         /* Check that we have enough samples. */
232         for ( ; ; )
233         {
234             p_buffer = p_fifo->p_first;
235             if ( p_buffer == NULL ) break;
236             if ( p_buffer->i_pts + p_buffer->i_length >= end_date ) break;
237
238             /* Check that all buffers are contiguous. */
239             prev_date = p_fifo->p_first->i_pts + p_fifo->p_first->i_length;
240             p_buffer = p_buffer->p_next;
241             b_drop_buffers = 0;
242             for ( ; p_buffer != NULL; p_buffer = p_buffer->p_next )
243             {
244                 if ( prev_date != p_buffer->i_pts )
245                 {
246                     msg_Warn( p_aout,
247                               "buffer hole, dropping packets (%"PRId64")",
248                               p_buffer->i_pts - prev_date );
249                     b_drop_buffers = 1;
250                     break;
251                 }
252                 if ( p_buffer->i_pts + p_buffer->i_length >= end_date ) break;
253                 prev_date = p_buffer->i_pts + p_buffer->i_length;
254             }
255             if ( b_drop_buffers )
256             {
257                 aout_buffer_t * p_deleted = p_fifo->p_first;
258                 while ( p_deleted != NULL && p_deleted != p_buffer )
259                 {
260                     aout_buffer_t * p_next = p_deleted->p_next;
261                     aout_BufferFree( p_deleted );
262                     p_deleted = p_next;
263                 }
264                 p_fifo->p_first = p_deleted; /* == p_buffer */
265             }
266             else break;
267         }
268         if ( p_buffer == NULL ) break;
269
270         p_buffer = p_fifo->p_first;
271         if ( !AOUT_FMT_NON_LINEAR( &p_aout->p_mixer->fmt ) )
272         {
273             /* Additionally check that p_first_byte_to_mix is well
274              * located. */
275             mtime_t i_buffer = (start_date - p_buffer->i_pts)
276                             * p_aout->p_mixer->fmt.i_bytes_per_frame
277                             * p_aout->p_mixer->fmt.i_rate
278                             / p_aout->p_mixer->fmt.i_frame_length
279                             / 1000000;
280             ptrdiff_t mixer_nb_bytes;
281
282             if ( p_input->mixer.begin == NULL )
283             {
284                 p_input->mixer.begin = p_buffer->p_buffer;
285             }
286             mixer_nb_bytes = p_input->mixer.begin - p_buffer->p_buffer;
287
288             if ( !((i_buffer + p_aout->p_mixer->fmt.i_bytes_per_frame
289                      > mixer_nb_bytes) &&
290                    (i_buffer < p_aout->p_mixer->fmt.i_bytes_per_frame
291                      + mixer_nb_bytes)) )
292             {
293                 msg_Warn( p_aout, "mixer start isn't output start (%"PRId64")",
294                           i_buffer - mixer_nb_bytes );
295
296                 /* Round to the nearest multiple */
297                 i_buffer /= p_aout->p_mixer->fmt.i_bytes_per_frame;
298                 i_buffer *= p_aout->p_mixer->fmt.i_bytes_per_frame;
299                 if( i_buffer < 0 )
300                 {
301                     /* Is it really the best way to do it ? */
302                     aout_lock_output_fifo( p_aout );
303                     aout_FifoSet( p_aout, &p_aout->output.fifo, 0 );
304                     date_Set( &exact_start_date, 0 );
305                     aout_unlock_output_fifo( p_aout );
306                     break;
307                 }
308
309                 p_input->mixer.begin = p_buffer->p_buffer + i_buffer;
310             }
311         }
312     }
313
314     if ( i < p_aout->i_nb_inputs || i_first_input == p_aout->i_nb_inputs )
315     {
316         /* Interrupted before the end... We can't run. */
317         aout_unlock_input_fifos( p_aout );
318         return -1;
319     }
320
321     /* Run the mixer. */
322     aout_buffer_t * p_outbuf;
323     p_outbuf = p_aout->p_mixer->mix( p_aout->p_mixer,
324                                      p_aout->output.i_nb_samples, volume );
325     aout_unlock_input_fifos( p_aout );
326
327     if( unlikely(p_outbuf == NULL) )
328         return -1;
329
330     p_outbuf->i_pts = start_date;
331     p_outbuf->i_length = end_date - start_date;
332     aout_OutputPlay( p_aout, p_outbuf );
333     return 0;
334 }
335
336 /*****************************************************************************
337  * aout_MixerRun: entry point for the mixer & post-filters processing
338  *****************************************************************************
339  * Please note that you must hold the mixer lock.
340  *****************************************************************************/
341 void aout_MixerRun( aout_instance_t * p_aout, float volume )
342 {
343     while( MixBuffer( p_aout, volume ) != -1 );
344 }