1 /*****************************************************************************
2 * portaudio.c : portaudio (v19) audio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002, 2006 the VideoLAN team
7 * Authors: Frederic Ruget <frederic.ruget@free.fr>
8 * Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
38 #include <portaudio.h>
40 #define FRAME_SIZE 1024 /* The size is in samples, not in bytes */
43 # define PORTAUDIO_IS_SERIOUSLY_BROKEN 1
46 /*****************************************************************************
47 * aout_sys_t: portaudio audio output method descriptor
48 *****************************************************************************/
49 typedef struct pa_thread_t
52 aout_instance_t
*p_aout
;
55 vlc_mutex_t lock_wait
;
58 vlc_mutex_t lock_signal
;
66 aout_instance_t
*p_aout
;
69 PaDeviceIndex i_devices
;
71 PaDeviceIndex i_device_id
;
72 const PaDeviceInfo
*deviceInfo
;
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
;
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 };
87 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
88 static bool b_init
= 0;
89 static pa_thread_t
*pa_thread
;
90 static void* PORTAUDIOThread( vlc_object_t
* );
93 /*****************************************************************************
95 *****************************************************************************/
96 static int Open ( vlc_object_t
* );
97 static void Close ( vlc_object_t
* );
98 static void Play ( aout_instance_t
* );
100 static int PAOpenDevice( aout_instance_t
* );
101 static int PAOpenStream( aout_instance_t
* );
103 /*****************************************************************************
105 *****************************************************************************/
106 #define DEVICE_TEXT N_("Output device")
107 #define DEVICE_LONGTEXT N_("Portaudio identifier for the output device")
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
)
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().
125 static int paCallback( const void *inputBuffer
, void *outputBuffer
,
126 unsigned long framesPerBuffer
,
127 const PaStreamCallbackTimeInfo
*paDate
,
128 PaStreamCallbackFlags statusFlags
, void *p_cookie
)
130 VLC_UNUSED( inputBuffer
); VLC_UNUSED( statusFlags
);
132 struct aout_sys_t
*p_sys
= (struct aout_sys_t
*) p_cookie
;
133 aout_instance_t
*p_aout
= p_sys
->p_aout
;
134 aout_buffer_t
*p_buffer
;
137 out_date
= mdate() + (mtime_t
) ( 1000000 *
138 ( paDate
->outputBufferDacTime
- paDate
->currentTime
) );
139 p_buffer
= aout_OutputNextBuffer( p_aout
, out_date
, true );
141 if ( p_buffer
!= NULL
)
143 if( p_sys
->b_chan_reorder
)
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
);
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.
158 aout_BufferFree( p_buffer
);
161 /* Audio output buffer shortage -> stop the fill process and wait */
163 vlc_memset( outputBuffer
, 0, framesPerBuffer
* p_sys
->i_sample_size
);
168 /*****************************************************************************
169 * Open: open the audio device
170 *****************************************************************************/
171 static int Open( vlc_object_t
* p_this
)
173 aout_instance_t
*p_aout
= (aout_instance_t
*)p_this
;
174 struct aout_sys_t
* p_sys
;
176 msg_Dbg( p_aout
, "entering Open()");
178 /* Allocate p_sys structure */
179 p_sys
= malloc( sizeof(aout_sys_t
) );
182 p_sys
->p_aout
= p_aout
;
184 p_aout
->output
.p_sys
= p_sys
;
185 p_aout
->output
.pf_play
= Play
;
187 /* Retrieve output device id from config */
188 p_sys
->i_device_id
= var_CreateGetInteger( p_aout
, "portaudio-audio-device" );
190 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
196 if( PAOpenDevice( p_aout
) != VLC_SUCCESS
)
198 msg_Err( p_aout
, "cannot open portaudio device" );
203 /* Close device for now. We'll re-open it later on */
204 if( ( i_err
= Pa_Terminate() ) != paNoError
)
206 msg_Err( p_aout
, "closing the device returned %d", i_err
);
211 /* Now we need to setup our DirectSound play notification structure */
212 pa_thread
= vlc_object_create( p_aout
, sizeof(pa_thread_t
) );
213 pa_thread
->p_aout
= p_aout
;
214 pa_thread
->b_error
= false;
215 vlc_mutex_init( &pa_thread
->lock_wait
);
216 vlc_cond_init( &pa_thread
->wait
);
217 pa_thread
->b_wait
= false;
218 vlc_mutex_init( &pa_thread
->lock_signal
);
219 vlc_cond_init( &pa_thread
->signal
);
220 pa_thread
->b_signal
= false;
222 /* Create PORTAUDIOThread */
223 if( vlc_thread_create( pa_thread
, "aout", PORTAUDIOThread
,
224 VLC_THREAD_PRIORITY_OUTPUT
) )
226 msg_Err( p_aout
, "cannot create PORTAUDIO thread" );
232 pa_thread
->p_aout
= p_aout
;
233 pa_thread
->b_wait
= false;
234 pa_thread
->b_signal
= false;
235 pa_thread
->b_error
= false;
238 /* Signal start of stream */
239 vlc_mutex_lock( &pa_thread
->lock_signal
);
240 pa_thread
->b_signal
= true;
241 vlc_cond_signal( &pa_thread
->signal
);
242 vlc_mutex_unlock( &pa_thread
->lock_signal
);
244 /* Wait until thread is ready */
245 vlc_mutex_lock( &pa_thread
->lock_wait
);
246 if( !pa_thread
->b_wait
)
247 vlc_cond_wait( &pa_thread
->wait
, &pa_thread
->lock_wait
);
248 vlc_mutex_unlock( &pa_thread
->lock_wait
);
249 pa_thread
->b_wait
= false;
251 if( pa_thread
->b_error
)
253 msg_Err( p_aout
, "PORTAUDIO thread failed" );
262 if( PAOpenDevice( p_aout
) != VLC_SUCCESS
)
264 msg_Err( p_aout
, "cannot open portaudio device" );
269 if( PAOpenStream( p_aout
) != VLC_SUCCESS
)
271 msg_Err( p_aout
, "cannot open portaudio device" );
279 /*****************************************************************************
280 * Close: close the audio device
281 *****************************************************************************/
282 static void Close ( vlc_object_t
*p_this
)
284 aout_instance_t
*p_aout
= (aout_instance_t
*)p_this
;
285 aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
287 msg_Dbg( p_aout
, "closing portaudio");
289 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
291 /* Signal end of stream */
292 vlc_mutex_lock( &pa_thread
->lock_signal
);
293 pa_thread
->b_signal
= true;
294 vlc_cond_signal( &pa_thread
->signal
);
295 vlc_mutex_unlock( &pa_thread
->lock_signal
);
297 /* Wait until thread is ready */
298 vlc_mutex_lock( &pa_thread
->lock_wait
);
299 if( !pa_thread
->b_wait
)
300 vlc_cond_wait( &pa_thread
->wait
, &pa_thread
->lock_wait
);
301 vlc_mutex_unlock( &pa_thread
->lock_wait
);
302 pa_thread
->b_wait
= false;
306 int i_err
= Pa_StopStream( p_sys
->p_stream
);
307 if( i_err
!= paNoError
)
309 msg_Err( p_aout
, "Pa_StopStream: %d (%s)", i_err
,
310 Pa_GetErrorText( i_err
) );
312 i_err
= Pa_CloseStream( p_sys
->p_stream
);
313 if( i_err
!= paNoError
)
315 msg_Err( p_aout
, "Pa_CloseStream: %d (%s)", i_err
,
316 Pa_GetErrorText( i_err
) );
319 i_err
= Pa_Terminate();
320 if( i_err
!= paNoError
)
322 msg_Err( p_aout
, "Pa_Terminate: %d (%s)", i_err
,
323 Pa_GetErrorText( i_err
) );
328 msg_Dbg( p_aout
, "portaudio closed");
332 static int PAOpenDevice( aout_instance_t
*p_aout
)
334 aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
335 const PaDeviceInfo
*p_pdi
;
337 vlc_value_t val
, text
;
340 /* Initialize portaudio */
341 i_err
= Pa_Initialize();
342 if( i_err
!= paNoError
)
344 msg_Err( p_aout
, "Pa_Initialize returned %d : %s",
345 i_err
, Pa_GetErrorText( i_err
) );
350 p_sys
->i_devices
= Pa_GetDeviceCount();
351 if( p_sys
->i_devices
< 0 )
353 i_err
= p_sys
->i_devices
;
354 msg_Err( p_aout
, "Pa_GetDeviceCount returned %d : %s", i_err
,
355 Pa_GetErrorText( i_err
) );
360 /* Display all devices info */
361 msg_Dbg( p_aout
, "number of devices = %d", p_sys
->i_devices
);
362 for( i
= 0; i
< p_sys
->i_devices
; i
++ )
364 p_pdi
= Pa_GetDeviceInfo( i
);
365 msg_Dbg( p_aout
, "------------------------------------- #%d", i
);
366 msg_Dbg( p_aout
, "Name = %s", p_pdi
->name
);
367 msg_Dbg( p_aout
, "Max Inputs = %d, Max Outputs = %d",
368 p_pdi
->maxInputChannels
, p_pdi
->maxOutputChannels
);
370 msg_Dbg( p_aout
, "-------------------------------------" );
372 msg_Dbg( p_aout
, "requested device is #%d", p_sys
->i_device_id
);
373 if( p_sys
->i_device_id
>= p_sys
->i_devices
)
375 msg_Err( p_aout
, "device %d does not exist", p_sys
->i_device_id
);
378 p_sys
->deviceInfo
= Pa_GetDeviceInfo( p_sys
->i_device_id
);
380 if( p_sys
->deviceInfo
->maxOutputChannels
< 1 )
382 msg_Err( p_aout
, "no channel available" );
386 if( var_Type( p_aout
, "audio-device" ) == 0 )
388 var_Create( p_aout
, "audio-device", VLC_VAR_INTEGER
|VLC_VAR_HASCHOICE
);
389 text
.psz_string
= _("Audio Device");
390 var_Change( p_aout
, "audio-device", VLC_VAR_SETTEXT
, &text
, NULL
);
392 if( p_sys
->deviceInfo
->maxOutputChannels
>= 1 )
394 val
.i_int
= AOUT_VAR_MONO
;
395 text
.psz_string
= _("Mono");
396 var_Change( p_aout
, "audio-device", VLC_VAR_ADDCHOICE
,
398 msg_Dbg( p_aout
, "device supports 1 channel" );
400 if( p_sys
->deviceInfo
->maxOutputChannels
>= 2 )
402 val
.i_int
= AOUT_VAR_STEREO
;
403 text
.psz_string
= _("Stereo");
404 var_Change( p_aout
, "audio-device", VLC_VAR_ADDCHOICE
,
406 var_Change( p_aout
, "audio-device", VLC_VAR_SETDEFAULT
,
408 var_Set( p_aout
, "audio-device", val
);
409 msg_Dbg( p_aout
, "device supports 2 channels" );
411 if( p_sys
->deviceInfo
->maxOutputChannels
>= 4 )
413 val
.i_int
= AOUT_VAR_2F2R
;
414 text
.psz_string
= _("2 Front 2 Rear");
415 var_Change( p_aout
, "audio-device", VLC_VAR_ADDCHOICE
,
417 msg_Dbg( p_aout
, "device supports 4 channels" );
419 if( p_sys
->deviceInfo
->maxOutputChannels
>= 5 )
421 val
.i_int
= AOUT_VAR_3F2R
;
422 text
.psz_string
= _("3 Front 2 Rear");
423 var_Change( p_aout
, "audio-device",
424 VLC_VAR_ADDCHOICE
, &val
, &text
);
425 msg_Dbg( p_aout
, "device supports 5 channels" );
427 if( p_sys
->deviceInfo
->maxOutputChannels
>= 6 )
429 val
.i_int
= AOUT_VAR_5_1
;
430 text
.psz_string
= _("5.1");
431 var_Change( p_aout
, "audio-device", VLC_VAR_ADDCHOICE
,
433 msg_Dbg( p_aout
, "device supports 5.1 channels" );
436 var_AddCallback( p_aout
, "audio-device", aout_ChannelsRestart
, NULL
);
437 var_SetBool( p_aout
, "intf-change", true );
440 /* Audio format is paFloat32 (always supported by portaudio v19) */
441 p_aout
->output
.output
.i_format
= VLC_CODEC_FL32
;
446 if( ( i_err
= Pa_Terminate() ) != paNoError
)
448 msg_Err( p_aout
, "Pa_Terminate returned %d", i_err
);
453 static int PAOpenStream( aout_instance_t
*p_aout
)
455 aout_sys_t
*p_sys
= p_aout
->output
.p_sys
;
456 const PaHostErrorInfo
* paLastHostErrorInfo
= Pa_GetLastHostErrorInfo();
457 PaStreamParameters paStreamParameters
;
459 int i_channels
, i_err
;
460 uint32_t i_channel_mask
;
462 if( var_Get( p_aout
, "audio-device", &val
) < 0 )
467 if( val
.i_int
== AOUT_VAR_5_1
)
469 p_aout
->output
.output
.i_physical_channels
470 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
471 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
474 else if( val
.i_int
== AOUT_VAR_3F2R
)
476 p_aout
->output
.output
.i_physical_channels
477 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
| AOUT_CHAN_CENTER
478 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
;
480 else if( val
.i_int
== AOUT_VAR_2F2R
)
482 p_aout
->output
.output
.i_physical_channels
483 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
484 | AOUT_CHAN_REARLEFT
| AOUT_CHAN_REARRIGHT
;
486 else if( val
.i_int
== AOUT_VAR_MONO
)
488 p_aout
->output
.output
.i_physical_channels
= AOUT_CHAN_CENTER
;
492 p_aout
->output
.output
.i_physical_channels
493 = AOUT_CHAN_LEFT
| AOUT_CHAN_RIGHT
;
496 i_channels
= aout_FormatNbChannels( &p_aout
->output
.output
);
497 msg_Dbg( p_aout
, "nb_channels requested = %d", i_channels
);
498 i_channel_mask
= p_aout
->output
.output
.i_physical_channels
;
500 /* Calculate the frame size in bytes */
501 p_sys
->i_sample_size
= 4 * i_channels
;
502 p_aout
->output
.i_nb_samples
= FRAME_SIZE
;
503 aout_FormatPrepare( &p_aout
->output
.output
);
504 aout_VolumeSoftInit( p_aout
);
506 /* Check for channel reordering */
507 p_aout
->output
.p_sys
->i_channel_mask
= i_channel_mask
;
508 p_aout
->output
.p_sys
->i_bits_per_sample
= 32; /* forced to paFloat32 */
509 p_aout
->output
.p_sys
->i_channels
= i_channels
;
511 p_aout
->output
.p_sys
->b_chan_reorder
=
512 aout_CheckChannelReorder( NULL
, pi_channels_out
,
513 i_channel_mask
, i_channels
,
514 p_aout
->output
.p_sys
->pi_chan_table
);
516 if( p_aout
->output
.p_sys
->b_chan_reorder
)
518 msg_Dbg( p_aout
, "channel reordering needed" );
521 paStreamParameters
.device
= p_sys
->i_device_id
;
522 paStreamParameters
.channelCount
= i_channels
;
523 paStreamParameters
.sampleFormat
= paFloat32
;
524 paStreamParameters
.suggestedLatency
=
525 p_sys
->deviceInfo
->defaultLowOutputLatency
;
526 paStreamParameters
.hostApiSpecificStreamInfo
= NULL
;
528 i_err
= Pa_OpenStream( &p_sys
->p_stream
, NULL
/* no input */,
529 &paStreamParameters
, (double)p_aout
->output
.output
.i_rate
,
530 FRAME_SIZE
, paClipOff
, paCallback
, p_sys
);
531 if( i_err
!= paNoError
)
533 msg_Err( p_aout
, "Pa_OpenStream returns %d : %s", i_err
,
534 Pa_GetErrorText( i_err
) );
535 if( i_err
== paUnanticipatedHostError
)
537 msg_Err( p_aout
, "type %d code %ld : %s",
538 paLastHostErrorInfo
->hostApiType
,
539 paLastHostErrorInfo
->errorCode
,
540 paLastHostErrorInfo
->errorText
);
546 i_err
= Pa_StartStream( p_sys
->p_stream
);
547 if( i_err
!= paNoError
)
549 msg_Err( p_aout
, "Pa_StartStream() failed" );
550 Pa_CloseStream( p_sys
->p_stream
);
557 /*****************************************************************************
559 *****************************************************************************/
560 static void Play( aout_instance_t
* p_aout
)
562 VLC_UNUSED( p_aout
);
565 #ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN
566 /*****************************************************************************
567 * PORTAUDIOThread: all interactions with libportaudio.a are handled
568 * in this single thread. Otherwise libportaudio.a is _not_ happy :-(
569 *****************************************************************************/
570 static void* PORTAUDIOThread( vlc_object_t
*p_this
)
572 pa_thread_t
*pa_thread
= (pa_thread_t
*)p_this
;
573 aout_instance_t
*p_aout
;
576 int canc
= vlc_savecancel ();
578 while( vlc_object_alive (pa_thread
) )
580 /* Wait for start of stream */
581 vlc_mutex_lock( &pa_thread
->lock_signal
);
582 if( !pa_thread
->b_signal
)
583 vlc_cond_wait( &pa_thread
->signal
, &pa_thread
->lock_signal
);
584 vlc_mutex_unlock( &pa_thread
->lock_signal
);
585 pa_thread
->b_signal
= false;
587 p_aout
= pa_thread
->p_aout
;
588 p_sys
= p_aout
->output
.p_sys
;
590 if( PAOpenDevice( p_aout
) != VLC_SUCCESS
)
592 msg_Err( p_aout
, "cannot open portaudio device" );
593 pa_thread
->b_error
= true;
596 if( !pa_thread
->b_error
&& PAOpenStream( p_aout
) != VLC_SUCCESS
)
598 msg_Err( p_aout
, "cannot open portaudio device" );
599 pa_thread
->b_error
= true;
601 i_err
= Pa_Terminate();
602 if( i_err
!= paNoError
)
604 msg_Err( p_aout
, "Pa_Terminate: %d (%s)", i_err
,
605 Pa_GetErrorText( i_err
) );
609 /* Tell the main thread that we are ready */
610 vlc_mutex_lock( &pa_thread
->lock_wait
);
611 pa_thread
->b_wait
= true;
612 vlc_cond_signal( &pa_thread
->wait
);
613 vlc_mutex_unlock( &pa_thread
->lock_wait
);
615 /* Wait for end of stream */
616 vlc_mutex_lock( &pa_thread
->lock_signal
);
617 if( !pa_thread
->b_signal
)
618 vlc_cond_wait( &pa_thread
->signal
, &pa_thread
->lock_signal
);
619 vlc_mutex_unlock( &pa_thread
->lock_signal
);
620 pa_thread
->b_signal
= false;
622 if( pa_thread
->b_error
) continue;
624 i_err
= Pa_StopStream( p_sys
->p_stream
);
625 if( i_err
!= paNoError
)
627 msg_Err( p_aout
, "Pa_StopStream: %d (%s)", i_err
,
628 Pa_GetErrorText( i_err
) );
630 i_err
= Pa_CloseStream( p_sys
->p_stream
);
631 if( i_err
!= paNoError
)
633 msg_Err( p_aout
, "Pa_CloseStream: %d (%s)", i_err
,
634 Pa_GetErrorText( i_err
) );
636 i_err
= Pa_Terminate();
637 if( i_err
!= paNoError
)
639 msg_Err( p_aout
, "Pa_Terminate: %d (%s)", i_err
,
640 Pa_GetErrorText( i_err
) );
643 /* Tell the main thread that we are ready */
644 vlc_mutex_lock( &pa_thread
->lock_wait
);
645 pa_thread
->b_wait
= true;
646 vlc_cond_signal( &pa_thread
->wait
);
647 vlc_mutex_unlock( &pa_thread
->lock_wait
);
649 vlc_restorecancel (canc
);