]> git.sesse.net Git - vlc/blob - modules/audio_output/portaudio.c
aout: pass audio buffer explicitly to pf_play
[vlc] / modules / audio_output / portaudio.c
1 /*****************************************************************************
2  * portaudio.c : portaudio (v19) audio output plugin
3  *****************************************************************************
4  * Copyright (C) 2002, 2006 the VideoLAN team
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., 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
37
38 #include <portaudio.h>
39
40 #define FRAME_SIZE 1024              /* The size is in samples, not in bytes */
41
42 #ifdef WIN32
43 #   define PORTAUDIO_IS_SERIOUSLY_BROKEN 1
44 #endif
45
46 /*****************************************************************************
47  * aout_sys_t: portaudio audio output method descriptor
48  *****************************************************************************/
49 typedef struct
50 {
51     audio_output_t *p_aout;
52
53     vlc_thread_t thread;
54     vlc_cond_t  wait;
55     vlc_mutex_t lock_wait;
56     bool  b_wait;
57     vlc_cond_t  signal;
58     vlc_mutex_t lock_signal;
59     bool  b_signal;
60     bool  b_error;
61
62 } pa_thread_t;
63
64 struct aout_sys_t
65 {
66     audio_output_t *p_aout;
67     PaStream *p_stream;
68
69     PaDeviceIndex i_devices;
70     int i_sample_size;
71     PaDeviceIndex i_device_id;
72     const PaDeviceInfo *deviceInfo;
73
74     bool b_chan_reorder;              /* do we need channel reordering */
75     int pi_chan_table[AOUT_CHAN_MAX];
76     uint32_t i_channel_mask;
77     uint32_t i_bits_per_sample;
78     uint32_t i_channels;
79 };
80
81 static const uint32_t pi_channels_out[] =
82     { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT,
83       AOUT_CHAN_CENTER, AOUT_CHAN_LFE,
84       AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER,
85       AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 };
86
87 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
88 static bool b_init = 0;
89 static pa_thread_t *pa_thread;
90 static void* PORTAUDIOThread( void * );
91 #endif
92
93 /*****************************************************************************
94  * Local prototypes.
95  *****************************************************************************/
96 static int  Open        ( vlc_object_t * );
97 static void Close       ( vlc_object_t * );
98 static void Play        ( audio_output_t *, block_t * );
99
100 static int PAOpenDevice( audio_output_t * );
101 static int PAOpenStream( audio_output_t * );
102
103 /*****************************************************************************
104  * Module descriptor
105  *****************************************************************************/
106 #define DEVICE_TEXT N_("Output device")
107 #define DEVICE_LONGTEXT N_("Portaudio identifier for the output device")
108
109 vlc_module_begin ()
110     set_shortname( "PortAudio" )
111     set_description( N_("PORTAUDIO audio output") )
112     set_category( CAT_AUDIO )
113     set_subcategory( SUBCAT_AUDIO_AOUT )
114     add_integer( "portaudio-audio-device", 0,
115                  DEVICE_TEXT, DEVICE_LONGTEXT, false )
116         add_deprecated_alias( "portaudio-device" )   /* deprecated since 0.9.3 */
117     set_capability( "audio output", 0 )
118     set_callbacks( Open, Close )
119 vlc_module_end ()
120
121 /* This routine will be called by the PortAudio engine when audio is needed.
122  * It may called at interrupt level on some machines so don't do anything
123  * that could mess up the system like calling malloc() or free().
124  */
125 static int paCallback( const void *inputBuffer, void *outputBuffer,
126                        unsigned long framesPerBuffer,
127                        const PaStreamCallbackTimeInfo *paDate,
128                        PaStreamCallbackFlags statusFlags, void *p_cookie )
129 {
130     VLC_UNUSED( inputBuffer ); VLC_UNUSED( statusFlags );
131
132     struct aout_sys_t *p_sys = (struct aout_sys_t*) p_cookie;
133     audio_output_t   *p_aout = p_sys->p_aout;
134     aout_buffer_t     *p_buffer;
135     mtime_t out_date;
136
137     out_date = mdate() + (mtime_t) ( 1000000 *
138         ( paDate->outputBufferDacTime - paDate->currentTime ) );
139     p_buffer = aout_OutputNextBuffer( p_aout, out_date, true );
140
141     if ( p_buffer != NULL )
142     {
143         if( p_sys->b_chan_reorder )
144         {
145             /* Do the channel reordering here */
146             aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer,
147                                  p_sys->i_channels, p_sys->pi_chan_table,
148                                  p_sys->i_bits_per_sample );
149         }
150         vlc_memcpy( outputBuffer, p_buffer->p_buffer,
151                     framesPerBuffer * p_sys->i_sample_size );
152         /* aout_BufferFree may be dangereous here, but then so is
153          * aout_OutputNextBuffer (calls aout_BufferFree internally).
154          * one solution would be to link the no longer useful buffers
155          * in a second fifo (in aout_OutputNextBuffer too) and to
156          * wait until we are in Play to do the actual free.
157          */
158         aout_BufferFree( p_buffer );
159     }
160     else
161         /* Audio output buffer shortage -> stop the fill process and wait */
162     {
163         vlc_memset( outputBuffer, 0, framesPerBuffer * p_sys->i_sample_size );
164     }
165     return 0;
166 }
167
168 /*****************************************************************************
169  * Open: open the audio device
170  *****************************************************************************/
171 static int Open( vlc_object_t * p_this )
172 {
173     audio_output_t *p_aout = (audio_output_t *)p_this;
174     struct aout_sys_t * p_sys;
175
176     msg_Dbg( p_aout, "entering Open()");
177
178     /* Allocate p_sys structure */
179     p_sys = malloc( sizeof(aout_sys_t) );
180     if( p_sys == NULL )
181         return VLC_ENOMEM;
182     p_sys->p_aout = p_aout;
183     p_sys->p_stream = 0;
184     p_aout->sys = p_sys;
185     p_aout->pf_play = Play;
186     p_aout->pf_pause = NULL;
187     p_aout->pf_flush = NULL;
188
189     /* Retrieve output device id from config */
190     p_sys->i_device_id = var_CreateGetInteger( p_aout, "portaudio-audio-device" );
191
192 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
193     if( !b_init )
194     {
195         int i_err;
196
197         /* Test device */
198         if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
199         {
200             msg_Err( p_aout, "cannot open portaudio device" );
201             free( p_sys );
202             return VLC_EGENERIC;
203         }
204
205         /* Close device for now. We'll re-open it later on */
206         if( ( i_err = Pa_Terminate() ) != paNoError )
207         {
208             msg_Err( p_aout, "closing the device returned %d", i_err );
209         }
210
211         b_init = true;
212
213         /* Now we need to setup our DirectSound play notification structure */
214         pa_thread = calloc( 1, sizeof(*pa_thread) );
215         pa_thread->p_aout = p_aout;
216         pa_thread->b_error = false;
217         vlc_mutex_init( &pa_thread->lock_wait );
218         vlc_cond_init( &pa_thread->wait );
219         pa_thread->b_wait = false;
220         vlc_mutex_init( &pa_thread->lock_signal );
221         vlc_cond_init( &pa_thread->signal );
222         pa_thread->b_signal = false;
223
224         /* Create PORTAUDIOThread */
225         if( vlc_clone( &pa_thread->thread, PORTAUDIOThread, pa_thread,
226                                VLC_THREAD_PRIORITY_OUTPUT ) )
227         {
228             msg_Err( p_aout, "cannot create PORTAUDIO thread" );
229             return VLC_EGENERIC;
230         }
231     }
232     else
233     {
234         pa_thread->p_aout = p_aout;
235         pa_thread->b_wait = false;
236         pa_thread->b_signal = false;
237         pa_thread->b_error = false;
238     }
239
240     /* Signal start of stream */
241     vlc_mutex_lock( &pa_thread->lock_signal );
242     pa_thread->b_signal = true;
243     vlc_cond_signal( &pa_thread->signal );
244     vlc_mutex_unlock( &pa_thread->lock_signal );
245
246     /* Wait until thread is ready */
247     vlc_mutex_lock( &pa_thread->lock_wait );
248     while( !pa_thread->b_wait )
249         vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait );
250     vlc_mutex_unlock( &pa_thread->lock_wait );
251     pa_thread->b_wait = false;
252
253     if( pa_thread->b_error )
254     {
255         msg_Err( p_aout, "PORTAUDIO thread failed" );
256         Close( p_this );
257         return VLC_EGENERIC;
258     }
259
260     return VLC_SUCCESS;
261
262 #else
263
264     if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
265     {
266         msg_Err( p_aout, "cannot open portaudio device" );
267         free( p_sys );
268         return VLC_EGENERIC;
269     }
270
271     if( PAOpenStream( p_aout ) != VLC_SUCCESS )
272     {
273         msg_Err( p_aout, "cannot open portaudio device" );
274     }
275
276     return VLC_SUCCESS;
277
278 #endif
279 }
280
281 /*****************************************************************************
282  * Close: close the audio device
283  *****************************************************************************/
284 static void Close ( vlc_object_t *p_this )
285 {
286     audio_output_t *p_aout = (audio_output_t *)p_this;
287     aout_sys_t *p_sys = p_aout->sys;
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 = 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     while( !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 = false;
305
306 #else
307
308     int 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( audio_output_t *p_aout )
335 {
336     aout_sys_t *p_sys = p_aout->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 = _("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 = _("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 = _("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 = _("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 = _("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         var_TriggerCallback( p_aout, "intf-change" );
440     }
441
442     /* Audio format is paFloat32 (always supported by portaudio v19) */
443     p_aout->format.i_format = VLC_CODEC_FL32;
444
445     return VLC_SUCCESS;
446
447  error:
448     if( ( i_err = Pa_Terminate() ) != paNoError )
449     {
450         msg_Err( p_aout, "Pa_Terminate returned %d", i_err );
451     }
452     return VLC_EGENERIC;
453 }
454
455 static int PAOpenStream( audio_output_t *p_aout )
456 {
457     aout_sys_t *p_sys = p_aout->sys;
458     const PaHostErrorInfo* paLastHostErrorInfo = Pa_GetLastHostErrorInfo();
459     PaStreamParameters paStreamParameters;
460     vlc_value_t val;
461     int i_channels, i_err;
462     uint32_t i_channel_mask;
463
464     if( var_Get( p_aout, "audio-device", &val ) < 0 )
465     {
466         return VLC_EGENERIC;
467     }
468
469     if( val.i_int == AOUT_VAR_5_1 )
470     {
471         p_aout->format.i_physical_channels
472             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
473               | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT
474               | AOUT_CHAN_LFE;
475     }
476     else if( val.i_int == AOUT_VAR_3F2R )
477     {
478         p_aout->format.i_physical_channels
479             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER
480             | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
481     }
482     else if( val.i_int == AOUT_VAR_2F2R )
483     {
484         p_aout->format.i_physical_channels
485             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT
486             | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
487     }
488     else if( val.i_int == AOUT_VAR_MONO )
489     {
490         p_aout->format.i_physical_channels = AOUT_CHAN_CENTER;
491     }
492     else
493     {
494         p_aout->format.i_physical_channels
495             = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
496     }
497
498     i_channels = aout_FormatNbChannels( &p_aout->format );
499     msg_Dbg( p_aout, "nb_channels requested = %d", i_channels );
500     i_channel_mask = p_aout->format.i_physical_channels;
501
502     /* Calculate the frame size in bytes */
503     p_sys->i_sample_size = 4 * i_channels;
504     p_aout->i_nb_samples = FRAME_SIZE;
505     aout_FormatPrepare( &p_aout->format );
506     aout_VolumeSoftInit( p_aout );
507
508     /* Check for channel reordering */
509     p_aout->sys->i_channel_mask = i_channel_mask;
510     p_aout->sys->i_bits_per_sample = 32; /* forced to paFloat32 */
511     p_aout->sys->i_channels = i_channels;
512
513     p_aout->sys->b_chan_reorder =
514         aout_CheckChannelReorder( NULL, pi_channels_out,
515                                   i_channel_mask, i_channels,
516                                   p_aout->sys->pi_chan_table );
517
518     if( p_aout->sys->b_chan_reorder )
519     {
520         msg_Dbg( p_aout, "channel reordering needed" );
521     }
522
523     paStreamParameters.device = p_sys->i_device_id;
524     paStreamParameters.channelCount = i_channels;
525     paStreamParameters.sampleFormat = paFloat32;
526     paStreamParameters.suggestedLatency =
527         p_sys->deviceInfo->defaultLowOutputLatency;
528     paStreamParameters.hostApiSpecificStreamInfo = NULL;
529
530     i_err = Pa_OpenStream( &p_sys->p_stream, NULL /* no input */,
531                 &paStreamParameters, (double)p_aout->format.i_rate,
532                 FRAME_SIZE, paClipOff, paCallback, p_sys );
533     if( i_err != paNoError )
534     {
535         msg_Err( p_aout, "Pa_OpenStream returns %d : %s", i_err,
536                  Pa_GetErrorText( i_err ) );
537         if( i_err == paUnanticipatedHostError )
538         {
539             msg_Err( p_aout, "type %d code %ld : %s",
540                      paLastHostErrorInfo->hostApiType,
541                      paLastHostErrorInfo->errorCode,
542                      paLastHostErrorInfo->errorText );
543         }
544         p_sys->p_stream = 0;
545         return VLC_EGENERIC;
546     }
547
548     i_err = Pa_StartStream( p_sys->p_stream );
549     if( i_err != paNoError )
550     {
551         msg_Err( p_aout, "Pa_StartStream() failed" );
552         Pa_CloseStream( p_sys->p_stream );
553         return VLC_EGENERIC;
554     }
555
556     return VLC_SUCCESS;
557 }
558
559 /*****************************************************************************
560  * Play: play sound
561  *****************************************************************************/
562 static void Play( audio_output_t * p_aout, block_t *block )
563 {
564     aout_FifoPush( &p_aout->fifo, block );
565 }
566
567 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
568 /*****************************************************************************
569  * PORTAUDIOThread: all interactions with libportaudio.a are handled
570  * in this single thread.  Otherwise libportaudio.a is _not_ happy :-(
571  *****************************************************************************/
572 static void* PORTAUDIOThread( void *data )
573 {
574     pa_thread_t *pa_thread = (pa_thread_t*)data;
575     audio_output_t *p_aout;
576     aout_sys_t *p_sys;
577     int i_err;
578     int canc = vlc_savecancel ();
579
580     for( ;; )
581     {
582         /* Wait for start of stream */
583         vlc_mutex_lock( &pa_thread->lock_signal );
584         while( !pa_thread->b_signal )
585             vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal );
586         vlc_mutex_unlock( &pa_thread->lock_signal );
587         pa_thread->b_signal = false;
588
589         p_aout = pa_thread->p_aout;
590         p_sys = p_aout->sys;
591
592         if( PAOpenDevice( p_aout ) != VLC_SUCCESS )
593         {
594             msg_Err( p_aout, "cannot open portaudio device" );
595             pa_thread->b_error = true;
596         }
597
598         if( !pa_thread->b_error && PAOpenStream( p_aout ) != VLC_SUCCESS )
599         {
600             msg_Err( p_aout, "cannot open portaudio device" );
601             pa_thread->b_error = true;
602
603             i_err = Pa_Terminate();
604             if( i_err != paNoError )
605             {
606                 msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
607                          Pa_GetErrorText( i_err ) );
608             }
609         }
610
611         /* Tell the main thread that we are ready */
612         vlc_mutex_lock( &pa_thread->lock_wait );
613         pa_thread->b_wait = true;
614         vlc_cond_signal( &pa_thread->wait );
615         vlc_mutex_unlock( &pa_thread->lock_wait );
616
617         /* Wait for end of stream */
618         vlc_mutex_lock( &pa_thread->lock_signal );
619         while( !pa_thread->b_signal )
620             vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal );
621         vlc_mutex_unlock( &pa_thread->lock_signal );
622         pa_thread->b_signal = false;
623
624         if( pa_thread->b_error ) continue;
625
626         i_err = Pa_StopStream( p_sys->p_stream );
627         if( i_err != paNoError )
628         {
629             msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err,
630                      Pa_GetErrorText( i_err ) );
631         }
632         i_err = Pa_CloseStream( p_sys->p_stream );
633         if( i_err != paNoError )
634         {
635             msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err,
636                      Pa_GetErrorText( i_err ) );
637         }
638         i_err = Pa_Terminate();
639         if( i_err != paNoError )
640         {
641             msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err,
642                      Pa_GetErrorText( i_err ) );
643         }
644
645         /* Tell the main thread that we are ready */
646         vlc_mutex_lock( &pa_thread->lock_wait );
647         pa_thread->b_wait = true;
648         vlc_cond_signal( &pa_thread->wait );
649         vlc_mutex_unlock( &pa_thread->lock_wait );
650     }
651     vlc_restorecancel (canc);
652     return NULL;
653 }
654 #endif