]> git.sesse.net Git - vlc/blob - modules/audio_output/portaudio.c
7135870048d38f41260faa5ae3a764804c04df75
[vlc] / modules / audio_output / portaudio.c
1 /*****************************************************************************
2  * portaudio.c : portaudio (v19) audio output plugin
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id$
6  *
7  * Authors: Frederic Ruget <frederic.ruget@free.fr>
8  *          Gildas Bazin <gbazin@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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <string.h>
29 #include <stdlib.h>
30
31 #include <vlc/vlc.h>
32 #include <vlc/aout.h>
33 #include <portaudio.h>
34
35 #include "aout_internal.h"
36
37 #define FRAME_SIZE 1024              /* The size is in samples, not in bytes */
38
39 #ifdef WIN32
40 #   define PORTAUDIO_IS_SERIOUSLY_BROKEN 1
41 #endif
42
43 /*****************************************************************************
44  * aout_sys_t: portaudio audio output method descriptor
45  *****************************************************************************/
46 typedef struct pa_thread_t
47 {
48     VLC_COMMON_MEMBERS
49     aout_instance_t *p_aout;
50
51     vlc_cond_t  wait;
52     vlc_mutex_t lock_wait;
53     vlc_bool_t  b_wait;
54     vlc_cond_t  signal;
55     vlc_mutex_t lock_signal;
56     vlc_bool_t  b_signal;
57
58 } pa_thread_t;
59
60 struct aout_sys_t
61 {
62     aout_instance_t *p_aout;
63     PaStream *p_stream;
64
65     PaDeviceIndex i_devices;
66     int i_sample_size;
67     PaDeviceIndex i_device_id;
68     const PaDeviceInfo *deviceInfo;
69
70     vlc_bool_t b_chan_reorder;              /* do we need channel reordering */
71     int pi_chan_table[AOUT_CHAN_MAX];
72     uint32_t i_channel_mask;
73     uint32_t i_bits_per_sample;
74     uint32_t i_channels;
75 };
76
77 static const uint32_t pi_channels_in[] =
78     { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
79       AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT,
80       AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
81       AOUT_CHAN_CENTER, AOUT_CHAN_LFE, 0 };
82 static const uint32_t pi_channels_out[] =
83     { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
84       AOUT_CHAN_CENTER, AOUT_CHAN_LFE,
85       AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT,
86       AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 };
87
88 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
89 static vlc_bool_t b_init = 0;
90 static pa_thread_t *pa_thread;
91 static void PORTAUDIOThread( pa_thread_t * );
92 #endif
93
94 /*****************************************************************************
95  * Local prototypes.
96  *****************************************************************************/
97 static int  Open        ( vlc_object_t * );
98 static void Close       ( vlc_object_t * );
99 static void Play        ( aout_instance_t * );
100
101 static int PAOpenDevice( aout_instance_t * );
102 static int PAOpenStream( aout_instance_t * );
103
104 /*****************************************************************************
105  * Module descriptor
106  *****************************************************************************/
107 #define DEVICE_TEXT N_("Output device")
108 #define DEVICE_LONGTEXT N_("Portaudio identifier for the output device")
109
110 vlc_module_begin();
111     set_description( N_("PORTAUDIO audio output") );
112     add_integer( "portaudio-device", 0, NULL,
113                  DEVICE_TEXT, DEVICE_LONGTEXT, VLC_FALSE );
114     set_capability( "audio output", 0 );
115     set_callbacks( Open, Close );
116 vlc_module_end();
117
118 /* This routine will be called by the PortAudio engine when audio is needed.
119  * It may called at interrupt level on some machines so don't do anything
120  * that could mess up the system like calling malloc() or free().
121  */
122 static int paCallback( const void *inputBuffer, void *outputBuffer,
123                        unsigned long framesPerBuffer,
124                        const PaStreamCallbackTimeInfo *paDate,
125                        PaStreamCallbackFlags statusFlags, void *p_cookie )
126 {
127     struct aout_sys_t *p_sys = (struct aout_sys_t*) p_cookie;
128     aout_instance_t   *p_aout = p_sys->p_aout;
129     aout_buffer_t     *p_buffer;
130     mtime_t out_date;
131
132     out_date = mdate() + (mtime_t) ( 1000000 *
133         ( paDate->outputBufferDacTime - paDate->currentTime ) );
134     p_buffer = aout_OutputNextBuffer( p_aout, out_date, VLC_TRUE );
135
136     if ( p_buffer != NULL )
137     {
138         if( p_sys->b_chan_reorder )
139         {
140             /* Do the channel reordering here */
141             aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes,
142                                  p_sys->i_channels, p_sys->pi_chan_table,
143                                  p_sys->i_bits_per_sample );
144         }
145         p_aout->p_vlc->pf_memcpy( outputBuffer, p_buffer->p_buffer,
146                                   framesPerBuffer * p_sys->i_sample_size );
147         /* aout_BufferFree may be dangereous here, but then so is
148          * aout_OutputNextBuffer (calls aout_BufferFree internally).
149          * one solution would be to link the no longer useful buffers
150          * in a second fifo (in aout_OutputNextBuffer too) and to
151          * wait until we are in Play to do the actual free.
152          */
153         aout_BufferFree( p_buffer );
154     }
155     else
156         /* Audio output buffer shortage -> stop the fill process and wait */
157     {
158         p_aout->p_vlc->pf_memset( outputBuffer, 0,
159                                   framesPerBuffer * p_sys->i_sample_size );
160     }
161     return 0;
162 }
163
164 /*****************************************************************************
165  * Open: open the audio device
166  *****************************************************************************/
167 static int Open( vlc_object_t * p_this )
168 {
169     aout_instance_t *p_aout = (aout_instance_t *)p_this;
170     struct aout_sys_t * p_sys;
171     vlc_value_t val;
172     int i_err;
173
174     msg_Dbg( p_aout, "Entering Open()");
175
176     /* Allocate p_sys structure */
177     p_sys = (aout_sys_t *)malloc( sizeof(aout_sys_t) );
178     if( p_sys == NULL )
179     {
180         msg_Err( p_aout, "out of memory" );
181         return VLC_ENOMEM;
182     }
183     p_sys->p_aout = p_aout;
184     p_sys->p_stream = 0;
185     p_aout->output.p_sys = p_sys;
186     p_aout->output.pf_play = Play;
187
188     /* Retrieve output device id from config */
189     var_Create( p_aout, "portaudio-device", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT);
190     var_Get( p_aout, "portaudio-device", &val );
191     p_sys->i_device_id = val.i_int;
192
193 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
194     if( !b_init )
195     {
196         /* Test device */
197         if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
198         {
199             msg_Err( p_aout, "cannot open portaudio device" );
200             free( p_sys );
201             return VLC_EGENERIC;
202         }
203
204         /* Close device for now. We'll re-open it later on */
205         if( ( i_err = Pa_Terminate() ) != paNoError )
206         {
207             msg_Err( p_aout, "Pa_Terminate returned %d", i_err );
208         }
209
210         b_init = VLC_TRUE;
211
212         /* Now we need to setup our DirectSound play notification structure */
213         pa_thread = vlc_object_create( p_aout, sizeof(pa_thread_t) );
214         pa_thread->p_aout = p_aout;
215         pa_thread->b_error = VLC_FALSE;
216         vlc_mutex_init( p_aout, &pa_thread->lock_wait );
217         vlc_cond_init( p_aout, &pa_thread->wait );
218         pa_thread->b_wait = VLC_FALSE;
219         vlc_mutex_init( p_aout, &pa_thread->lock_signal );
220         vlc_cond_init( p_aout, &pa_thread->signal );
221         pa_thread->b_signal = VLC_FALSE;
222
223         /* Create PORTAUDIOThread */
224         if( vlc_thread_create( pa_thread, "aout", PORTAUDIOThread,
225                                VLC_THREAD_PRIORITY_OUTPUT, VLC_FALSE ) )
226         {
227             msg_Err( p_aout, "cannot create PORTAUDIO thread" );
228             return VLC_EGENERIC;
229         }
230     }
231     else
232     {
233         pa_thread->p_aout = p_aout;
234         pa_thread->b_wait = VLC_FALSE;
235         pa_thread->b_signal = VLC_FALSE;
236         pa_thread->b_error = VLC_FALSE;
237     }
238
239     /* Signal start of stream */
240     vlc_mutex_lock( &pa_thread->lock_signal );
241     pa_thread->b_signal = VLC_TRUE;
242     vlc_cond_signal( &pa_thread->signal );
243     vlc_mutex_unlock( &pa_thread->lock_signal );
244
245     /* Wait until thread is ready */
246     vlc_mutex_lock( &pa_thread->lock_wait );
247     if( !pa_thread->b_wait )
248         vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait );
249     vlc_mutex_unlock( &pa_thread->lock_wait );
250     pa_thread->b_wait = VLC_FALSE;
251
252     if( pa_thread->b_error )
253     {
254         msg_Err( p_aout, "PORTAUDIO thread failed" );
255         Close( p_this );
256         return VLC_EGENERIC;
257     }
258
259     return VLC_SUCCESS;
260
261 #else
262
263     if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
264     {
265         msg_Err( p_aout, "cannot open portaudio device" );
266         free( p_sys );
267         return VLC_EGENERIC;
268     }
269
270     if( PAOpenStream( p_aout ) != VLC_SUCCESS )
271     {
272         msg_Err( p_aout, "cannot open portaudio device" );
273     }
274
275     return VLC_SUCCESS;
276
277 #endif
278 }
279
280 /*****************************************************************************
281  * Close: close the audio device
282  *****************************************************************************/
283 static void Close ( vlc_object_t *p_this )
284 {
285     aout_instance_t *p_aout = (aout_instance_t *)p_this;
286     aout_sys_t *p_sys = p_aout->output.p_sys;
287     int i_err;
288
289     msg_Dbg( p_aout, "closing portaudio");
290
291 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
292
293     /* Signal end of stream */
294     vlc_mutex_lock( &pa_thread->lock_signal );
295     pa_thread->b_signal = VLC_TRUE;
296     vlc_cond_signal( &pa_thread->signal );
297     vlc_mutex_unlock( &pa_thread->lock_signal );
298
299     /* Wait until thread is ready */
300     vlc_mutex_lock( &pa_thread->lock_wait );
301     if( !pa_thread->b_wait )
302         vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait );
303     vlc_mutex_unlock( &pa_thread->lock_wait );
304     pa_thread->b_wait = VLC_FALSE;
305
306 #else
307
308     i_err = Pa_StopStream( p_sys->p_stream );
309     if( i_err != paNoError )
310     {
311         msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err,
312                  Pa_GetErrorText( i_err ) );
313     }
314     i_err = Pa_CloseStream( p_sys->p_stream );
315     if( i_err != paNoError )
316     {
317         msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err,
318                  Pa_GetErrorText( i_err ) );
319     }
320
321     i_err = Pa_Terminate();
322     if( i_err != paNoError )
323     {
324         msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
325                  Pa_GetErrorText( i_err ) );
326     }
327
328 #endif
329
330     msg_Dbg( p_aout, "portaudio closed");
331     free( p_sys );
332 }
333
334 static int PAOpenDevice( aout_instance_t *p_aout )
335 {
336     aout_sys_t *p_sys = p_aout->output.p_sys;
337     const PaDeviceInfo *p_pdi;
338     PaError i_err;
339     vlc_value_t val, text;
340     int i;
341
342     /* Initialize portaudio */
343     i_err = Pa_Initialize();
344     if( i_err != paNoError )
345     {
346         msg_Err( p_aout, "Pa_Initialize returned %d : %s",
347                  i_err, Pa_GetErrorText( i_err ) );
348
349         return VLC_EGENERIC;
350     }
351
352     p_sys->i_devices = Pa_GetDeviceCount();
353     if( p_sys->i_devices < 0 )
354     {
355         i_err = p_sys->i_devices;
356         msg_Err( p_aout, "Pa_GetDeviceCount returned %d : %s", i_err,
357                  Pa_GetErrorText( i_err ) );
358
359         goto error;
360     }
361
362     /* Display all devices info */
363     msg_Dbg( p_aout, "number of devices = %d", p_sys->i_devices );
364     for( i = 0; i < p_sys->i_devices; i++ )
365     {
366         p_pdi = Pa_GetDeviceInfo( i );
367         msg_Dbg( p_aout, "------------------------------------- #%d", i );
368         msg_Dbg( p_aout, "Name         = %s", p_pdi->name );
369         msg_Dbg( p_aout, "Max Inputs   = %d, Max Outputs = %d",
370                   p_pdi->maxInputChannels, p_pdi->maxOutputChannels );
371     }
372     msg_Dbg( p_aout, "-------------------------------------" );
373
374     msg_Dbg( p_aout, "requested device is #%d", p_sys->i_device_id );
375     if( p_sys->i_device_id >= p_sys->i_devices )
376     {
377         msg_Err( p_aout, "device %d does not exist", p_sys->i_device_id );
378         goto error;
379     }
380     p_sys->deviceInfo = Pa_GetDeviceInfo( p_sys->i_device_id );
381
382     if( p_sys->deviceInfo->maxOutputChannels < 1 )
383     {
384         msg_Err( p_aout, "no channel available" );
385         goto error;
386     }
387
388     if( var_Type( p_aout, "audio-device" ) == 0 )
389     {
390         var_Create( p_aout, "audio-device", VLC_VAR_INTEGER|VLC_VAR_HASCHOICE);
391         text.psz_string = _("Audio Device");
392         var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
393
394         if( p_sys->deviceInfo->maxOutputChannels >= 1 )
395         {
396             val.i_int = AOUT_VAR_MONO;
397             text.psz_string = N_("Mono");
398             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
399                         &val, &text );
400             msg_Dbg( p_aout, "device supports 1 channel" );
401         }
402         if( p_sys->deviceInfo->maxOutputChannels >= 2 )
403         {
404             val.i_int = AOUT_VAR_STEREO;
405             text.psz_string = N_("Stereo");
406             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
407                         &val, &text );
408             var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT,
409                         &val, NULL );
410             var_Set( p_aout, "audio-device", val );
411             msg_Dbg( p_aout, "device supports 2 channels" );
412         }
413         if( p_sys->deviceInfo->maxOutputChannels >= 4 )
414         {
415             val.i_int = AOUT_VAR_2F2R;
416             text.psz_string = N_("2 Front 2 Rear");
417             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
418                         &val, &text );
419             msg_Dbg( p_aout, "device supports 4 channels" );
420         }
421         if( p_sys->deviceInfo->maxOutputChannels >= 5 )
422         {
423             val.i_int = AOUT_VAR_3F2R;
424             text.psz_string = N_("3 Front 2 Rear");
425             var_Change( p_aout, "audio-device",
426                         VLC_VAR_ADDCHOICE, &val, &text );
427             msg_Dbg( p_aout, "device supports 5 channels" );
428         }
429         if( p_sys->deviceInfo->maxOutputChannels >= 6 )
430         {
431             val.i_int = AOUT_VAR_5_1;
432             text.psz_string = N_("5.1");
433             var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE,
434                         &val, &text );
435             msg_Dbg( p_aout, "device supports 5.1 channels" );
436         }
437
438         var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL );
439
440         val.b_bool = VLC_TRUE;
441         var_Set( p_aout, "intf-change", val );
442     }
443
444     /* Audio format is paFloat32 (always supported by portaudio v19) */
445     p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
446
447     return VLC_SUCCESS;
448
449  error:
450     if( ( i_err = Pa_Terminate() ) != paNoError )
451     {
452         msg_Err( p_aout, "Pa_Terminate returned %d", i_err );
453     }
454     return VLC_EGENERIC;
455 }
456
457 static int PAOpenStream( aout_instance_t *p_aout )
458 {
459     aout_sys_t *p_sys = p_aout->output.p_sys;
460     const PaHostErrorInfo* paLastHostErrorInfo = Pa_GetLastHostErrorInfo();
461     PaStreamParameters paStreamParameters;
462     vlc_value_t val;
463     int i_channels, i_err;
464     uint32_t i_channel_mask;
465
466     if( var_Get( p_aout, "audio-device", &val ) < 0 )
467     {
468         return VLC_EGENERIC;
469     }
470
471     if( val.i_int == AOUT_VAR_5_1 )
472     {
473         p_aout->output.output.i_physical_channels
474             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
475               | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
476               | AOUT_CHAN_LFE;
477     }
478     else if( val.i_int == AOUT_VAR_3F2R )
479     {
480         p_aout->output.output.i_physical_channels
481             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
482             | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
483     }
484     else if( val.i_int == AOUT_VAR_2F2R )
485     {
486         p_aout->output.output.i_physical_channels
487             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
488             | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
489     }
490     else if( val.i_int == AOUT_VAR_MONO )
491     {
492         p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
493     }
494     else
495     {
496         p_aout->output.output.i_physical_channels
497             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
498     }
499
500     i_channels = aout_FormatNbChannels( &p_aout->output.output );
501     msg_Dbg( p_aout, "nb_channels requested = %d", i_channels );
502     i_channel_mask = p_aout->output.output.i_physical_channels;
503
504     /* Calculate the frame size in bytes */
505     p_sys->i_sample_size = 4 * i_channels;
506     p_aout->output.i_nb_samples = FRAME_SIZE;
507     aout_FormatPrepare( &p_aout->output.output );
508     aout_VolumeSoftInit( p_aout );
509
510     /* Check for channel reordering */
511     p_aout->output.p_sys->i_channel_mask = i_channel_mask;
512     p_aout->output.p_sys->i_bits_per_sample = 32; /* forced to paFloat32 */
513     p_aout->output.p_sys->i_channels = i_channels;
514
515     p_aout->output.p_sys->b_chan_reorder =
516         aout_CheckChannelReorder( pi_channels_in, pi_channels_out,
517                                   i_channel_mask, i_channels,
518                                   p_aout->output.p_sys->pi_chan_table );
519
520     if( p_aout->output.p_sys->b_chan_reorder )
521     {
522         msg_Dbg( p_aout, "channel reordering needed" );
523     }
524
525     paStreamParameters.device = p_sys->i_device_id;
526     paStreamParameters.channelCount = i_channels;
527     paStreamParameters.sampleFormat = paFloat32;
528     paStreamParameters.suggestedLatency =
529         p_sys->deviceInfo->defaultLowOutputLatency;
530     paStreamParameters.hostApiSpecificStreamInfo = NULL;
531
532     i_err = Pa_OpenStream( &p_sys->p_stream, NULL /* no input */,
533                 &paStreamParameters, (double)p_aout->output.output.i_rate,
534                 FRAME_SIZE, paClipOff, paCallback, p_sys );
535     if( i_err != paNoError )
536     {
537         msg_Err( p_aout, "Pa_OpenStream returns %d : %s", i_err,
538                  Pa_GetErrorText( i_err ) );
539         if( i_err == paUnanticipatedHostError )
540         {
541             msg_Err( p_aout, "type %d code %ld : %s",
542                      paLastHostErrorInfo->hostApiType,
543                      paLastHostErrorInfo->errorCode,
544                      paLastHostErrorInfo->errorText );
545         }
546         p_sys->p_stream = 0;
547         return VLC_EGENERIC;
548     }
549
550     i_err = Pa_StartStream( p_sys->p_stream );
551     if( i_err != paNoError )
552     {
553         msg_Err( p_aout, "Pa_StartStream() failed" );
554         Pa_CloseStream( p_sys->p_stream );
555         return VLC_EGENERIC;
556     }
557
558     return VLC_SUCCESS;
559 }
560
561 /*****************************************************************************
562  * Play: play sound
563  *****************************************************************************/
564 static void Play( aout_instance_t * p_aout )
565 {
566 }
567
568 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
569 /*****************************************************************************
570  * PORTAUDIOThread: all interactions with libportaudio.a are handled
571  * in this single thread.  Otherwise libportaudio.a is _not_ happy :-(
572  *****************************************************************************/
573 static void PORTAUDIOThread( pa_thread_t *pa_thread )
574 {
575     aout_instance_t *p_aout;
576     aout_sys_t *p_sys;
577     int i_err;
578
579     while( !pa_thread->b_die )
580     {
581         /* Wait for start of stream */
582         vlc_mutex_lock( &pa_thread->lock_signal );
583         if( !pa_thread->b_signal )
584             vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal );
585         vlc_mutex_unlock( &pa_thread->lock_signal );
586         pa_thread->b_signal = VLC_FALSE;
587
588         p_aout = pa_thread->p_aout;
589         p_sys = p_aout->output.p_sys;
590
591         if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
592         {
593             msg_Err( p_aout, "cannot open portaudio device" );
594             pa_thread->b_error = VLC_TRUE;
595         }
596
597         if( !pa_thread->b_error && PAOpenStream( p_aout ) != VLC_SUCCESS )
598         {
599             msg_Err( p_aout, "cannot open portaudio device" );
600             pa_thread->b_error = VLC_TRUE;
601
602             i_err = Pa_Terminate();
603             if( i_err != paNoError )
604             {
605                 msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
606                          Pa_GetErrorText( i_err ) );
607             }
608         }
609
610         /* Tell the main thread that we are ready */
611         vlc_mutex_lock( &pa_thread->lock_wait );
612         pa_thread->b_wait = VLC_TRUE;
613         vlc_cond_signal( &pa_thread->wait );
614         vlc_mutex_unlock( &pa_thread->lock_wait );
615
616         /* Wait for end of stream */
617         vlc_mutex_lock( &pa_thread->lock_signal );
618         if( !pa_thread->b_signal )
619             vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal );
620         vlc_mutex_unlock( &pa_thread->lock_signal );
621         pa_thread->b_signal = VLC_FALSE;
622
623         if( pa_thread->b_error ) continue;
624
625         i_err = Pa_StopStream( p_sys->p_stream );
626         if( i_err != paNoError )
627         {
628             msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err,
629                      Pa_GetErrorText( i_err ) );
630         }
631         i_err = Pa_CloseStream( p_sys->p_stream );
632         if( i_err != paNoError )
633         {
634             msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err,
635                      Pa_GetErrorText( i_err ) );
636         }
637         i_err = Pa_Terminate();
638         if( i_err != paNoError )
639         {
640             msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
641                      Pa_GetErrorText( i_err ) );
642         }
643
644         /* Tell the main thread that we are ready */
645         vlc_mutex_lock( &pa_thread->lock_wait );
646         pa_thread->b_wait = VLC_TRUE;
647         vlc_cond_signal( &pa_thread->wait );
648         vlc_mutex_unlock( &pa_thread->lock_wait );
649     }
650 }
651 #endif