X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Fportaudio.c;h=2c6a0ea1dfb0e9f2fddc5979ed853f30ad24c56f;hb=41e32860383c388fa252775572d89328d1755e26;hp=5569b9b82877c24d8079eee10c74e17fb3100920;hpb=7ba99302dfb9bb582560988b3fc56632ca8a5834;p=vlc diff --git a/modules/audio_output/portaudio.c b/modules/audio_output/portaudio.c index 5569b9b828..2c6a0ea1df 100644 --- a/modules/audio_output/portaudio.c +++ b/modules/audio_output/portaudio.c @@ -1,7 +1,7 @@ /***************************************************************************** * portaudio.c : portaudio (v19) audio output plugin ***************************************************************************** - * Copyright (C) 2002 VideoLAN + * Copyright (C) 2002, 2006 the VideoLAN team * $Id$ * * Authors: Frederic Ruget @@ -11,7 +11,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -19,30 +19,45 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include -#include -#include -#include -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + -#include "aout_internal.h" +#include #define FRAME_SIZE 1024 /* The size is in samples, not in bytes */ +#ifdef WIN32 +# define PORTAUDIO_IS_SERIOUSLY_BROKEN 1 +#endif + /***************************************************************************** * aout_sys_t: portaudio audio output method descriptor *****************************************************************************/ typedef struct pa_thread_t { VLC_COMMON_MEMBERS - aout_instance_t * p_aout; + aout_instance_t *p_aout; + + vlc_cond_t wait; + vlc_mutex_t lock_wait; + bool b_wait; + vlc_cond_t signal; + vlc_mutex_t lock_signal; + bool b_signal; + bool b_error; } pa_thread_t; @@ -56,19 +71,31 @@ struct aout_sys_t PaDeviceIndex i_device_id; const PaDeviceInfo *deviceInfo; - vlc_mutex_t lock; - vlc_cond_t wait; - - pa_thread_t *pa_thread; + bool b_chan_reorder; /* do we need channel reordering */ + int pi_chan_table[AOUT_CHAN_MAX]; + uint32_t i_channel_mask; + uint32_t i_bits_per_sample; + uint32_t i_channels; }; +static const uint32_t pi_channels_out[] = + { AOUT_CHAN_LEFT, AOUT_CHAN_RIGHT, + AOUT_CHAN_CENTER, AOUT_CHAN_LFE, + AOUT_CHAN_REARLEFT, AOUT_CHAN_REARRIGHT, AOUT_CHAN_REARCENTER, + AOUT_CHAN_MIDDLELEFT, AOUT_CHAN_MIDDLERIGHT, 0 }; + +#ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN +static bool b_init = 0; +static pa_thread_t *pa_thread; +static void* PORTAUDIOThread( vlc_object_t * ); +#endif + /***************************************************************************** * Local prototypes. *****************************************************************************/ static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); static void Play ( aout_instance_t * ); -static void PORTAUDIOThread( pa_thread_t * ); static int PAOpenDevice( aout_instance_t * ); static int PAOpenStream( aout_instance_t * ); @@ -79,24 +106,29 @@ static int PAOpenStream( aout_instance_t * ); #define DEVICE_TEXT N_("Output device") #define DEVICE_LONGTEXT N_("Portaudio identifier for the output device") -vlc_module_begin(); - set_description( N_("PORTAUDIO audio output") ); - add_integer( "portaudio-device", 0, NULL, - DEVICE_TEXT, DEVICE_LONGTEXT, VLC_FALSE ); - set_capability( "audio output", 0 ); - set_callbacks( Open, Close ); -vlc_module_end(); +vlc_module_begin () + set_shortname( "PortAudio" ) + set_description( N_("PORTAUDIO audio output") ) + set_category( CAT_AUDIO ) + set_subcategory( SUBCAT_AUDIO_AOUT ) + add_integer( "portaudio-audio-device", 0, + DEVICE_TEXT, DEVICE_LONGTEXT, false ) + add_deprecated_alias( "portaudio-device" ) /* deprecated since 0.9.3 */ + set_capability( "audio output", 0 ) + set_callbacks( Open, Close ) +vlc_module_end () /* This routine will be called by the PortAudio engine when audio is needed. * It may called at interrupt level on some machines so don't do anything * that could mess up the system like calling malloc() or free(). */ static int paCallback( const void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *paDate, - PaStreamCallbackFlags statusFlags, - void *p_cookie ) + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *paDate, + PaStreamCallbackFlags statusFlags, void *p_cookie ) { + VLC_UNUSED( inputBuffer ); VLC_UNUSED( statusFlags ); + struct aout_sys_t *p_sys = (struct aout_sys_t*) p_cookie; aout_instance_t *p_aout = p_sys->p_aout; aout_buffer_t *p_buffer; @@ -104,12 +136,19 @@ static int paCallback( const void *inputBuffer, void *outputBuffer, out_date = mdate() + (mtime_t) ( 1000000 * ( paDate->outputBufferDacTime - paDate->currentTime ) ); - p_buffer = aout_OutputNextBuffer( p_aout, out_date, VLC_TRUE ); + p_buffer = aout_OutputNextBuffer( p_aout, out_date, true ); if ( p_buffer != NULL ) { - p_aout->p_vlc->pf_memcpy( outputBuffer, p_buffer->p_buffer, - framesPerBuffer * p_sys->i_sample_size ); + if( p_sys->b_chan_reorder ) + { + /* Do the channel reordering here */ + aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_buffer, + p_sys->i_channels, p_sys->pi_chan_table, + p_sys->i_bits_per_sample ); + } + vlc_memcpy( outputBuffer, p_buffer->p_buffer, + framesPerBuffer * p_sys->i_sample_size ); /* aout_BufferFree may be dangereous here, but then so is * aout_OutputNextBuffer (calls aout_BufferFree internally). * one solution would be to link the no longer useful buffers @@ -121,8 +160,7 @@ static int paCallback( const void *inputBuffer, void *outputBuffer, else /* Audio output buffer shortage -> stop the fill process and wait */ { - p_aout->p_vlc->pf_memset( outputBuffer, 0, - framesPerBuffer * p_sys->i_sample_size ); + vlc_memset( outputBuffer, 0, framesPerBuffer * p_sys->i_sample_size ); } return 0; } @@ -134,64 +172,108 @@ static int Open( vlc_object_t * p_this ) { aout_instance_t *p_aout = (aout_instance_t *)p_this; struct aout_sys_t * p_sys; - vlc_value_t val; - int i_err; - msg_Dbg( p_aout, "Entering Open()"); + msg_Dbg( p_aout, "entering Open()"); /* Allocate p_sys structure */ - p_sys = (aout_sys_t *)malloc( sizeof(aout_sys_t) ); + p_sys = malloc( sizeof(aout_sys_t) ); if( p_sys == NULL ) - { - msg_Err( p_aout, "out of memory" ); return VLC_ENOMEM; - } p_sys->p_aout = p_aout; p_sys->p_stream = 0; p_aout->output.p_sys = p_sys; p_aout->output.pf_play = Play; /* Retrieve output device id from config */ - var_Create( p_aout, "portaudio-device", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT); - var_Get( p_aout, "portaudio-device", &val ); - p_sys->i_device_id = val.i_int; + p_sys->i_device_id = var_CreateGetInteger( p_aout, "portaudio-audio-device" ); - if( PAOpenDevice( p_aout ) != VLC_SUCCESS ) +#ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN + if( !b_init ) { - msg_Err( p_aout, "cannot open portaudio device" ); - free( p_sys ); - return VLC_EGENERIC; - } + int i_err; - /* Close device for now. We'll re-open it later on */ - if( ( i_err = Pa_Terminate() ) != paNoError ) + /* Test device */ + if( PAOpenDevice( p_aout ) != VLC_SUCCESS ) + { + msg_Err( p_aout, "cannot open portaudio device" ); + free( p_sys ); + return VLC_EGENERIC; + } + + /* Close device for now. We'll re-open it later on */ + if( ( i_err = Pa_Terminate() ) != paNoError ) + { + msg_Err( p_aout, "closing the device returned %d", i_err ); + } + + b_init = true; + + /* Now we need to setup our DirectSound play notification structure */ + pa_thread = vlc_object_create( p_aout, sizeof(pa_thread_t) ); + pa_thread->p_aout = p_aout; + pa_thread->b_error = false; + vlc_mutex_init( &pa_thread->lock_wait ); + vlc_cond_init( &pa_thread->wait ); + pa_thread->b_wait = false; + vlc_mutex_init( &pa_thread->lock_signal ); + vlc_cond_init( &pa_thread->signal ); + pa_thread->b_signal = false; + + /* Create PORTAUDIOThread */ + if( vlc_thread_create( pa_thread, PORTAUDIOThread, + VLC_THREAD_PRIORITY_OUTPUT ) ) + { + msg_Err( p_aout, "cannot create PORTAUDIO thread" ); + return VLC_EGENERIC; + } + } + else { - msg_Err( p_aout, "Pa_Terminate returned %d", i_err ); + pa_thread->p_aout = p_aout; + pa_thread->b_wait = false; + pa_thread->b_signal = false; + pa_thread->b_error = false; } - /* Now we need to setup our DirectSound play notification structure */ - p_sys->pa_thread = vlc_object_create( p_aout, sizeof(pa_thread_t) ); - p_sys->pa_thread->p_aout = p_aout; + /* Signal start of stream */ + vlc_mutex_lock( &pa_thread->lock_signal ); + pa_thread->b_signal = true; + vlc_cond_signal( &pa_thread->signal ); + vlc_mutex_unlock( &pa_thread->lock_signal ); - vlc_mutex_init( p_aout, &p_sys->lock ); - vlc_cond_init( p_aout, &p_sys->wait ); + /* Wait until thread is ready */ + vlc_mutex_lock( &pa_thread->lock_wait ); + if( !pa_thread->b_wait ) + vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait ); + vlc_mutex_unlock( &pa_thread->lock_wait ); + pa_thread->b_wait = false; - /* Create PORTAUDIOThread */ - if( vlc_thread_create( p_sys->pa_thread, "aout", PORTAUDIOThread, - VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) ) + if( pa_thread->b_error ) { - msg_Err( p_aout, "cannot create PORTAUDIO thread" ); + msg_Err( p_aout, "PORTAUDIO thread failed" ); + Close( p_this ); return VLC_EGENERIC; } - if( p_sys->pa_thread->b_error ) + return VLC_SUCCESS; + +#else + + if( PAOpenDevice( p_aout ) != VLC_SUCCESS ) { - msg_Err( p_aout, "PORTAUDIO thread failed" ); - Close( p_this ); + msg_Err( p_aout, "cannot open portaudio device" ); + free( p_sys ); return VLC_EGENERIC; } + if( PAOpenStream( p_aout ) != VLC_SUCCESS ) + { + msg_Err( p_aout, "cannot open portaudio device" ); + } + return VLC_SUCCESS; + +#endif } /***************************************************************************** @@ -204,60 +286,24 @@ static void Close ( vlc_object_t *p_this ) msg_Dbg( p_aout, "closing portaudio"); - vlc_mutex_lock( &p_sys->lock ); - p_sys->pa_thread->b_die = VLC_TRUE; - vlc_cond_signal( &p_sys->wait ); - vlc_mutex_unlock( &p_sys->lock ); +#ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN - vlc_thread_join( p_sys->pa_thread ); - vlc_cond_destroy( &p_sys->wait ); - vlc_mutex_destroy( &p_sys->lock ); + /* Signal end of stream */ + vlc_mutex_lock( &pa_thread->lock_signal ); + pa_thread->b_signal = true; + vlc_cond_signal( &pa_thread->signal ); + vlc_mutex_unlock( &pa_thread->lock_signal ); - msg_Dbg( p_aout, "portaudio closed"); - free( p_sys ); -} + /* Wait until thread is ready */ + vlc_mutex_lock( &pa_thread->lock_wait ); + if( !pa_thread->b_wait ) + vlc_cond_wait( &pa_thread->wait, &pa_thread->lock_wait ); + vlc_mutex_unlock( &pa_thread->lock_wait ); + pa_thread->b_wait = false; -/***************************************************************************** - * Play: play sound - *****************************************************************************/ -static void Play( aout_instance_t * p_aout ) -{ -} +#else -/***************************************************************************** - * PORTAUDIOThread: all interactions with libportaudio.a are handled - * in this single thread. Otherwise libportaudio.a is _not_ happy :-( - *****************************************************************************/ -static void PORTAUDIOThread( pa_thread_t *pa_thread ) -{ - aout_instance_t *p_aout = pa_thread->p_aout; - aout_sys_t *p_sys = p_aout->output.p_sys; - int i_err; - - if( PAOpenDevice( p_aout ) != VLC_SUCCESS ) - { - msg_Err( p_aout, "cannot open portaudio device" ); - pa_thread->b_error = VLC_TRUE; - vlc_thread_ready( pa_thread ); - return; - } - - if( PAOpenStream( p_aout ) != VLC_SUCCESS ) - { - msg_Err( p_aout, "cannot open portaudio device" ); - pa_thread->b_error = VLC_TRUE; - vlc_thread_ready( pa_thread ); - goto end; - } - - /* Tell the main thread that we are ready */ - vlc_thread_ready( pa_thread ); - - vlc_mutex_lock( &p_sys->lock ); - if( !pa_thread->b_die ) vlc_cond_wait( &p_sys->wait, &p_sys->lock ); - vlc_mutex_unlock( &p_sys->lock ); - - i_err = Pa_StopStream( p_sys->p_stream ); + int i_err = Pa_StopStream( p_sys->p_stream ); if( i_err != paNoError ) { msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err, @@ -270,13 +316,17 @@ static void PORTAUDIOThread( pa_thread_t *pa_thread ) Pa_GetErrorText( i_err ) ); } - end: i_err = Pa_Terminate(); if( i_err != paNoError ) { msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err, Pa_GetErrorText( i_err ) ); } + +#endif + + msg_Dbg( p_aout, "portaudio closed"); + free( p_sys ); } static int PAOpenDevice( aout_instance_t *p_aout ) @@ -342,7 +392,7 @@ static int PAOpenDevice( aout_instance_t *p_aout ) if( p_sys->deviceInfo->maxOutputChannels >= 1 ) { val.i_int = AOUT_VAR_MONO; - text.psz_string = N_("Mono"); + text.psz_string = _("Mono"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); msg_Dbg( p_aout, "device supports 1 channel" ); @@ -350,17 +400,18 @@ static int PAOpenDevice( aout_instance_t *p_aout ) if( p_sys->deviceInfo->maxOutputChannels >= 2 ) { val.i_int = AOUT_VAR_STEREO; - text.psz_string = N_("Stereo"); + text.psz_string = _("Stereo"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); var_Change( p_aout, "audio-device", VLC_VAR_SETDEFAULT, &val, NULL ); + var_Set( p_aout, "audio-device", val ); msg_Dbg( p_aout, "device supports 2 channels" ); } if( p_sys->deviceInfo->maxOutputChannels >= 4 ) { val.i_int = AOUT_VAR_2F2R; - text.psz_string = N_("2 Front 2 Rear"); + text.psz_string = _("2 Front 2 Rear"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); msg_Dbg( p_aout, "device supports 4 channels" ); @@ -368,7 +419,7 @@ static int PAOpenDevice( aout_instance_t *p_aout ) if( p_sys->deviceInfo->maxOutputChannels >= 5 ) { val.i_int = AOUT_VAR_3F2R; - text.psz_string = N_("3 Front 2 Rear"); + text.psz_string = _("3 Front 2 Rear"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); msg_Dbg( p_aout, "device supports 5 channels" ); @@ -376,20 +427,18 @@ static int PAOpenDevice( aout_instance_t *p_aout ) if( p_sys->deviceInfo->maxOutputChannels >= 6 ) { val.i_int = AOUT_VAR_5_1; - text.psz_string = N_("5.1"); + text.psz_string = _("5.1"); var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text ); msg_Dbg( p_aout, "device supports 5.1 channels" ); } var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); - - val.b_bool = VLC_TRUE; - var_Set( p_aout, "intf-change", val ); + var_SetBool( p_aout, "intf-change", true ); } /* Audio format is paFloat32 (always supported by portaudio v19) */ - p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2'); + p_aout->output.output.i_format = VLC_CODEC_FL32; return VLC_SUCCESS; @@ -408,6 +457,7 @@ static int PAOpenStream( aout_instance_t *p_aout ) PaStreamParameters paStreamParameters; vlc_value_t val; int i_channels, i_err; + uint32_t i_channel_mask; if( var_Get( p_aout, "audio-device", &val ) < 0 ) { @@ -445,6 +495,7 @@ static int PAOpenStream( aout_instance_t *p_aout ) i_channels = aout_FormatNbChannels( &p_aout->output.output ); msg_Dbg( p_aout, "nb_channels requested = %d", i_channels ); + i_channel_mask = p_aout->output.output.i_physical_channels; /* Calculate the frame size in bytes */ p_sys->i_sample_size = 4 * i_channels; @@ -452,6 +503,21 @@ static int PAOpenStream( aout_instance_t *p_aout ) aout_FormatPrepare( &p_aout->output.output ); aout_VolumeSoftInit( p_aout ); + /* Check for channel reordering */ + p_aout->output.p_sys->i_channel_mask = i_channel_mask; + p_aout->output.p_sys->i_bits_per_sample = 32; /* forced to paFloat32 */ + p_aout->output.p_sys->i_channels = i_channels; + + p_aout->output.p_sys->b_chan_reorder = + aout_CheckChannelReorder( NULL, pi_channels_out, + i_channel_mask, i_channels, + p_aout->output.p_sys->pi_chan_table ); + + if( p_aout->output.p_sys->b_chan_reorder ) + { + msg_Dbg( p_aout, "channel reordering needed" ); + } + paStreamParameters.device = p_sys->i_device_id; paStreamParameters.channelCount = i_channels; paStreamParameters.sampleFormat = paFloat32; @@ -487,3 +553,100 @@ static int PAOpenStream( aout_instance_t *p_aout ) return VLC_SUCCESS; } + +/***************************************************************************** + * Play: play sound + *****************************************************************************/ +static void Play( aout_instance_t * p_aout ) +{ + VLC_UNUSED( p_aout ); +} + +#ifdef PORTAUDIO_IS_SERIOUSLY_BROKEN +/***************************************************************************** + * PORTAUDIOThread: all interactions with libportaudio.a are handled + * in this single thread. Otherwise libportaudio.a is _not_ happy :-( + *****************************************************************************/ +static void* PORTAUDIOThread( vlc_object_t *p_this ) +{ + pa_thread_t *pa_thread = (pa_thread_t*)p_this; + aout_instance_t *p_aout; + aout_sys_t *p_sys; + int i_err; + int canc = vlc_savecancel (); + + while( vlc_object_alive (pa_thread) ) + { + /* Wait for start of stream */ + vlc_mutex_lock( &pa_thread->lock_signal ); + if( !pa_thread->b_signal ) + vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal ); + vlc_mutex_unlock( &pa_thread->lock_signal ); + pa_thread->b_signal = false; + + p_aout = pa_thread->p_aout; + p_sys = p_aout->output.p_sys; + + if( PAOpenDevice( p_aout ) != VLC_SUCCESS ) + { + msg_Err( p_aout, "cannot open portaudio device" ); + pa_thread->b_error = true; + } + + if( !pa_thread->b_error && PAOpenStream( p_aout ) != VLC_SUCCESS ) + { + msg_Err( p_aout, "cannot open portaudio device" ); + pa_thread->b_error = true; + + i_err = Pa_Terminate(); + if( i_err != paNoError ) + { + msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err, + Pa_GetErrorText( i_err ) ); + } + } + + /* Tell the main thread that we are ready */ + vlc_mutex_lock( &pa_thread->lock_wait ); + pa_thread->b_wait = true; + vlc_cond_signal( &pa_thread->wait ); + vlc_mutex_unlock( &pa_thread->lock_wait ); + + /* Wait for end of stream */ + vlc_mutex_lock( &pa_thread->lock_signal ); + if( !pa_thread->b_signal ) + vlc_cond_wait( &pa_thread->signal, &pa_thread->lock_signal ); + vlc_mutex_unlock( &pa_thread->lock_signal ); + pa_thread->b_signal = false; + + if( pa_thread->b_error ) continue; + + i_err = Pa_StopStream( p_sys->p_stream ); + if( i_err != paNoError ) + { + msg_Err( p_aout, "Pa_StopStream: %d (%s)", i_err, + Pa_GetErrorText( i_err ) ); + } + i_err = Pa_CloseStream( p_sys->p_stream ); + if( i_err != paNoError ) + { + msg_Err( p_aout, "Pa_CloseStream: %d (%s)", i_err, + Pa_GetErrorText( i_err ) ); + } + i_err = Pa_Terminate(); + if( i_err != paNoError ) + { + msg_Err( p_aout, "Pa_Terminate: %d (%s)", i_err, + Pa_GetErrorText( i_err ) ); + } + + /* Tell the main thread that we are ready */ + vlc_mutex_lock( &pa_thread->lock_wait ); + pa_thread->b_wait = true; + vlc_cond_signal( &pa_thread->wait ); + vlc_mutex_unlock( &pa_thread->lock_wait ); + } + vlc_restorecancel (canc); + return NULL; +} +#endif