]> git.sesse.net Git - vlc/blob - src/audio_output/aout_pcm.c
* ./include/vlc_threads.h, ./src/misc/threads.c: improved the cond_wait
[vlc] / src / audio_output / aout_pcm.c
1 /*****************************************************************************
2  * aout_pcm.c: PCM audio output functions
3  *****************************************************************************
4  * Copyright (C) 1999-2002 VideoLAN
5  * $Id: aout_pcm.c,v 1.8 2002/06/01 12:32:01 sam Exp $
6  *
7  * Authors: Michel Kaempf <maxx@via.ecp.fr>
8  *          Cyril Deguet <asmax@via.ecp.fr>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                            /* calloc(), malloc(), free() */
29 #include <string.h>
30
31 #include <vlc/vlc.h>
32
33 #include "audio_output.h"
34 #include "aout_pcm.h"
35
36 /* Biggest difference allowed between scheduled playing date and actual date 
37    (in microseconds) */
38 #define MAX_DELTA 50000
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static void FillBuffer( aout_thread_t * p_aout, aout_fifo_t * p_fifo );
44 static int  NextFrame ( aout_thread_t * p_aout, aout_fifo_t * p_fifo,
45                         mtime_t aout_date );
46
47  /*****************************************************************************
48  * Functions
49  *****************************************************************************/
50 void aout_PCMThread( aout_thread_t * p_aout )
51 {
52     int i_fifo;
53     int i_buffer, i_buffer_limit, i_units = 0;
54
55 #if defined(WIN32)
56     if( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL) )
57         msg_Warn( p_aout, "could not change priority of aout_PCMThread()" );
58 #endif
59
60     /* As the s32_buffer was created with calloc(), we don't have to set this
61      * memory to zero and we can immediately jump into the thread's loop */
62     while ( ! p_aout->b_die )
63     {
64         vlc_mutex_lock( &p_aout->fifos_lock );
65         for ( i_fifo = 0; i_fifo < AOUT_MAX_FIFOS; i_fifo++ )
66         {
67             if( p_aout->fifo[i_fifo].b_die )
68             {
69                 aout_FreeFifo( &p_aout->fifo[i_fifo] );
70             }
71             else
72             {
73                 FillBuffer( p_aout, &p_aout->fifo[i_fifo] );
74             }
75         }
76         vlc_mutex_unlock( &p_aout->fifos_lock );
77
78         i_buffer_limit = p_aout->i_units * p_aout->i_channels;
79
80         switch ( p_aout->i_format )
81         {
82         case AOUT_FMT_U8:
83             for ( i_buffer = 0; i_buffer < i_buffer_limit; i_buffer++ )
84             {
85                 ((u8*)p_aout->buffer)[i_buffer] = (u8)(
86                   (p_aout->s32_buffer[i_buffer] / AOUT_MAX_FIFOS / 256 + 128)
87                      * p_aout->i_volume / 256);
88                  p_aout->s32_buffer[i_buffer] = 0;
89             }
90             break;
91
92         case AOUT_FMT_S8:
93             for ( i_buffer = 0; i_buffer < i_buffer_limit; i_buffer++ )
94             {
95                 ((s8*)p_aout->buffer)[i_buffer] = (s8)(
96                   p_aout->s32_buffer[i_buffer] / AOUT_MAX_FIFOS / 256
97                      * p_aout->i_volume / 256);
98                  p_aout->s32_buffer[i_buffer] = 0;
99             }
100             break;
101
102         case AOUT_FMT_U16_LE:
103         case AOUT_FMT_U16_BE:
104             for ( i_buffer = 0; i_buffer < i_buffer_limit; i_buffer++ )
105             {
106                 ((u16*)p_aout->buffer)[i_buffer] = (u16)(
107                   (p_aout->s32_buffer[i_buffer] / AOUT_MAX_FIFOS + 128)
108                      * p_aout->i_volume / 256);
109                  p_aout->s32_buffer[i_buffer] = 0;
110             }
111             break;
112
113         case AOUT_FMT_S16_LE:
114         case AOUT_FMT_S16_BE:
115             for ( i_buffer = 0; i_buffer < i_buffer_limit; i_buffer++ )
116             {
117                 ((s16*)p_aout->buffer)[i_buffer] = (s16)(
118                   p_aout->s32_buffer[i_buffer] / AOUT_MAX_FIFOS
119                      * p_aout->i_volume / 256);
120                  p_aout->s32_buffer[i_buffer] = 0;
121             }
122             break;
123         }
124
125         switch ( p_aout->i_format )
126         {
127         case AOUT_FMT_U8:
128         case AOUT_FMT_S8:
129             i_units = p_aout->pf_getbufinfo( p_aout, i_buffer_limit );
130
131             p_aout->date = mdate() + ((((mtime_t)((i_units +
132                 p_aout->i_latency) / p_aout->i_channels)) * 1000000) /
133                 ((mtime_t)p_aout->i_rate)) + p_aout->p_vlc->i_desync;
134
135             p_aout->pf_play( p_aout, (byte_t *)p_aout->buffer,
136                              i_buffer_limit );
137             break;
138
139         case AOUT_FMT_U16_LE:
140         case AOUT_FMT_U16_BE:
141         case AOUT_FMT_S16_LE:
142         case AOUT_FMT_S16_BE:
143             i_units = p_aout->pf_getbufinfo( p_aout, i_buffer_limit * 2 ) / 2;
144
145             p_aout->date = mdate() + ((((mtime_t)((i_units +
146                 p_aout->i_latency / 2) / p_aout->i_channels)) * 1000000) /
147                 ((mtime_t)p_aout->i_rate)) + p_aout->p_vlc->i_desync;
148
149             p_aout->pf_play( p_aout, (byte_t *)p_aout->buffer,
150                              i_buffer_limit * 2 );
151             break;
152         }
153
154         /* Sleep until there is only AOUT_BUFFER_DURATION/2 worth of audio
155          * left to play in the aout plugin, then we can start refill the
156          * plugin's buffer */
157         if( i_units > (i_buffer_limit/2) )
158             msleep( (i_units - i_buffer_limit/2) * AOUT_BUFFER_DURATION
159                     / i_buffer_limit );
160     }
161
162     vlc_mutex_lock( &p_aout->fifos_lock );
163     for ( i_fifo = 0; i_fifo < AOUT_MAX_FIFOS; i_fifo++ )
164     {
165         aout_FreeFifo( &p_aout->fifo[i_fifo] );
166     }
167     vlc_mutex_unlock( &p_aout->fifos_lock );
168 }
169
170 /* Following functions are local */
171
172 /*****************************************************************************
173  * InitializeIncrement: change i_x/i_y to i_a+i_b/i_c
174  *****************************************************************************/
175 static inline void InitializeIncrement( aout_increment_t *p_inc,
176                                         int i_x, int i_y )
177 {
178     p_inc->i_r = -i_y;
179     p_inc->i_a = 0;
180
181     while ( i_x >= i_y )
182     {
183         p_inc->i_a++;
184         i_x -= i_y;
185     }
186
187     p_inc->i_b = i_x;
188     p_inc->i_c = i_y;
189 }
190
191 /*****************************************************************************
192  * UpdateIncrement
193  *****************************************************************************/
194 static inline void UpdateIncrement( aout_increment_t *p_inc, int *pi_integer )
195 {
196     if( (p_inc->i_r += p_inc->i_b) >= 0 )
197     {
198         *pi_integer += p_inc->i_a + 1;
199         p_inc->i_r -= p_inc->i_c;
200     }
201     else
202     {
203         *pi_integer += p_inc->i_a;
204     }
205 }
206
207 /*****************************************************************************
208  * FillBuffer: Read data from decoder fifo, and put it in S32_buffer
209  *****************************************************************************/
210 static void FillBuffer( aout_thread_t * p_aout, aout_fifo_t * p_fifo )
211 {
212     int i_buffer = 0;
213     int i_buffer_limit, i_units;
214
215     switch ( p_fifo->i_format )
216     {
217     case AOUT_FIFO_PCM:
218
219         i_units = p_aout->i_units;
220
221         /* While there are empty frames in the buffer, fill them */
222         while ( i_units > 0 )
223         {
224             /* If there is no next frame, wait for one */
225             if( !p_fifo->b_next_frame )
226             {
227                 if( NextFrame(p_aout, p_fifo, p_aout->date + 
228                         ((((mtime_t)(i_buffer >> 1)) * 1000000) / 
229                         ((mtime_t)p_aout->i_rate))) )
230                 {
231                     break;
232                 }
233             }
234
235             i_buffer_limit = p_aout->i_units * p_aout->i_channels;
236
237             while ( i_buffer < i_buffer_limit )
238             {
239                 /* FIXME: make this more generic */
240                 if( p_aout->i_channels == 2 )
241                 {
242                     p_aout->s32_buffer[i_buffer++] +=
243                         (s32)( ((s16 *)p_fifo->buffer)[2*p_fifo->i_unit] );
244                     p_aout->s32_buffer[i_buffer++] +=
245                         (s32)( ((s16 *)p_fifo->buffer)[2*p_fifo->i_unit+1] );
246                 }
247                 else if( p_aout->i_channels == 1 )
248                 {
249                     p_aout->s32_buffer[i_buffer++] +=
250                         (s32)( ((s16 *)p_fifo->buffer)[p_fifo->i_unit] );
251                 }
252                 else
253                 {
254                     msg_Err( p_aout, "unsupported number of channels" );
255                 }
256
257                 UpdateIncrement(&p_fifo->unit_increment, &p_fifo->i_unit);
258
259                 if( p_fifo->i_unit >= p_fifo->i_unit_limit )
260                 {
261                     p_fifo->i_unit -= p_fifo->i_unit_limit;
262                 }
263             }
264  
265             if( p_fifo->i_units > i_units )
266             {
267                 p_fifo->i_units -= i_units;
268                 break;
269             }
270             else
271             {
272                 i_units -= p_fifo->i_units;
273
274                 vlc_mutex_lock( &p_fifo->data_lock );
275                 p_fifo->i_start_frame = p_fifo->i_next_frame;
276              /* p_fifo->b_start_frame = 1; */
277                 p_fifo->i_next_frame = AOUT_FIFO_INC( p_fifo->i_next_frame );
278                 p_fifo->b_next_frame = 0;
279                 vlc_cond_signal( &p_fifo->data_wait );
280                 vlc_mutex_unlock( &p_fifo->data_lock );
281
282             }
283         }
284         break;
285
286     default:
287         break;
288     }
289 }
290
291 static int NextFrame( aout_thread_t * p_aout, aout_fifo_t * p_fifo,
292                       mtime_t aout_date )
293 {
294     int i_units, i_units_dist, i_rate;
295     mtime_t i_delta;    
296
297     /* We take the lock */
298     vlc_mutex_lock( &p_fifo->data_lock );
299     if ( p_fifo->b_die )
300     {
301         vlc_mutex_unlock( &p_fifo->data_lock );
302         return( -1 );
303     }
304
305     /* Are we looking for a dated start frame ? */
306     if ( !p_fifo->b_start_frame )
307     {
308         while ( p_fifo->i_start_frame != p_fifo->i_end_frame )
309         {
310             if ( p_fifo->date[p_fifo->i_start_frame] != LAST_MDATE )
311             {
312                 p_fifo->b_start_frame = 1;
313                 p_fifo->i_next_frame = AOUT_FIFO_INC( p_fifo->i_start_frame );
314                 p_fifo->i_unit = p_fifo->i_start_frame * 
315                         (p_fifo->i_frame_size / p_fifo->i_channels);
316                 break;
317             }
318             p_fifo->i_start_frame = AOUT_FIFO_INC( p_fifo->i_start_frame );
319         }
320
321         if ( p_fifo->i_start_frame == p_fifo->i_end_frame )
322         {
323             vlc_mutex_unlock( &p_fifo->data_lock );
324             return( -1 );
325         }
326     }
327
328     /* We are looking for the next dated frame */
329     /* FIXME : is the output fifo full ?? -- pretty unlikely given its size */
330     while ( !p_fifo->b_next_frame )
331     {
332         while ( p_fifo->i_next_frame != p_fifo->i_end_frame )
333         {
334             if ( p_fifo->date[p_fifo->i_next_frame] != LAST_MDATE )
335             {
336                 p_fifo->b_next_frame = 1;
337                 break;
338             }
339             p_fifo->i_next_frame = AOUT_FIFO_INC( p_fifo->i_next_frame );
340         }
341
342         while ( p_fifo->i_next_frame == p_fifo->i_end_frame )
343         {
344             vlc_cond_wait( &p_fifo->data_wait, &p_fifo->data_lock );
345             if ( p_fifo->b_die )
346             {
347                 vlc_mutex_unlock( &p_fifo->data_lock );
348                 return( -1 );
349             }
350         }
351     }
352
353     i_units = ((p_fifo->i_next_frame - p_fifo->i_start_frame) & AOUT_FIFO_SIZE)
354                * (p_fifo->i_frame_size / p_fifo->i_channels);
355
356     i_delta = aout_date - p_fifo->date[p_fifo->i_start_frame];
357
358     /* Resample if delta is too long */
359     if( abs(i_delta) > MAX_DELTA )
360     {
361         i_rate = p_fifo->i_rate + (i_delta / 256);
362     }
363     else
364     {
365         i_rate = p_fifo->i_rate;
366     }
367
368     InitializeIncrement( &p_fifo->unit_increment, i_rate, p_aout->i_rate );
369
370     /* Calculate how many units we're going to write */
371     i_units_dist = p_fifo->i_unit - p_fifo->i_start_frame
372                                 * (p_fifo->i_frame_size / p_fifo->i_channels);
373
374     /* Manage the fifo wrapping */
375     if( i_units_dist > p_fifo->i_unit_limit / 2 )
376     {
377         i_units -= (i_units_dist - p_fifo->i_unit_limit);
378     }
379     else if( i_units_dist < - p_fifo->i_unit_limit / 2 )
380     {
381         i_units -= (i_units_dist + p_fifo->i_unit_limit);
382     }
383     else
384     {
385         i_units -= i_units_dist;
386     }
387
388     p_fifo->i_units = 1 + ( i_units * p_aout->i_rate / i_rate );
389
390     /* We release the lock before leaving */
391     vlc_mutex_unlock( &p_fifo->data_lock );
392     return( 0 );
393 }
394