X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faudio_output%2Fjack.c;h=f87d4cbe011ed09390e4c97b8a011f6f949f0106;hb=fa61862474c332412e0341d98f1cf82c067d277a;hp=a03580ac4e3f0c5f4ede5e1441b2d8eb0e4d71a4;hpb=d3fe7f28797d4dba65ffcdd60bf932e758a48a9e;p=vlc diff --git a/modules/audio_output/jack.c b/modules/audio_output/jack.c index a03580ac4e..f87d4cbe01 100644 --- a/modules/audio_output/jack.c +++ b/modules/audio_output/jack.c @@ -4,7 +4,8 @@ * Copyright (C) 2006 the VideoLAN team * $Id$ * - * Authors: Cyril Deguet + * Authors: Cyril Deguet + * Jon Griffiths * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,19 +21,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ - +/** + * \file modules/audio_output/jack.c + * \brief JACK audio output functions + */ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* strerror() */ #include /* write(), close() */ -#include /* calloc(), malloc(), free() */ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include #include #include +typedef jack_default_audio_sample_t jack_sample_t; + /***************************************************************************** * aout_sys_t: JACK audio output method descriptor ***************************************************************************** @@ -41,9 +50,10 @@ *****************************************************************************/ struct aout_sys_t { - jack_client_t *p_jack_client; - jack_port_t *p_jack_port[2]; - unsigned int i_channels; + jack_client_t *p_jack_client; + jack_port_t **p_jack_ports; + jack_sample_t **p_jack_buffers; + unsigned int i_channels; }; /***************************************************************************** @@ -52,18 +62,34 @@ struct aout_sys_t static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); static void Play ( aout_instance_t * ); -static int Process ( jack_nframes_t i_frames, void *p_arg ); +static int Process ( jack_nframes_t i_frames, void *p_arg ); + +#define AUTO_CONNECT_OPTION "jack-auto-connect" +#define AUTO_CONNECT_TEXT N_("Automatically connect to writable clients") +#define AUTO_CONNECT_LONGTEXT N_( \ + "If enabled, this option will automatically connect sound output to the " \ + "first writable JACK clients found." ) + +#define CONNECT_REGEX_OPTION "jack-connect-regex" +#define CONNECT_REGEX_TEXT N_("Connect to clients matching") +#define CONNECT_REGEX_LONGTEXT N_( \ + "If automatic connection is enabled, only JACK clients whose names " \ + "match this regular expression will be considered for connection." ) /***************************************************************************** * Module descriptor *****************************************************************************/ vlc_module_begin(); - set_shortname( "JACK" ); - set_description( _("JACK audio output") ); - set_capability( "audio output", 100 ); + set_shortname( "JACK" ); + set_description( N_("JACK audio output") ); + set_capability( "audio output", 100 ); set_category( CAT_AUDIO ); set_subcategory( SUBCAT_AUDIO_AOUT ); - set_callbacks( Open, Close ); + add_bool( AUTO_CONNECT_OPTION, 0, NULL, AUTO_CONNECT_TEXT, + AUTO_CONNECT_LONGTEXT, true ); + add_string( CONNECT_REGEX_OPTION, NULL, NULL, CONNECT_REGEX_TEXT, + CONNECT_REGEX_LONGTEXT, true ); + set_callbacks( Open, Close ); vlc_module_end(); /***************************************************************************** @@ -71,27 +97,31 @@ vlc_module_end(); *****************************************************************************/ static int Open( vlc_object_t *p_this ) { + char psz_name[32]; aout_instance_t *p_aout = (aout_instance_t *)p_this; - unsigned int i, i_in_ports; - const char **pp_in_ports; - struct aout_sys_t * p_sys; + struct aout_sys_t *p_sys = NULL; + int status = VLC_SUCCESS; + unsigned int i; + int i_error; /* Allocate structure */ - p_sys = malloc( sizeof( aout_sys_t ) ); + p_sys = calloc( 1, sizeof( aout_sys_t ) ); if( p_sys == NULL ) { - msg_Err( p_aout, "out of memory" ); - return VLC_ENOMEM; + status = VLC_ENOMEM; + goto error_out; } p_aout->output.p_sys = p_sys; /* Connect to the JACK server */ - p_sys->p_jack_client = jack_client_new( "vlc" ); + snprintf( psz_name, sizeof(psz_name), "vlc_%d", getpid()); + psz_name[sizeof(psz_name) - 1] = '\0'; + p_sys->p_jack_client = jack_client_new( psz_name ); if( p_sys->p_jack_client == NULL ) { msg_Err( p_aout, "failed to connect to JACK server" ); - free( p_sys ); - return VLC_EGENERIC; + status = VLC_EGENERIC; + goto error_out; } /* Set the process callback */ @@ -100,7 +130,7 @@ static int Open( vlc_object_t *p_this ) p_aout->output.pf_play = Play; aout_VolumeSoftInit( p_aout ); - /* JACK only support fl32 format */ + /* JACK only supports fl32 format */ p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2'); // TODO add buffer size callback p_aout->output.i_nb_samples = jack_get_buffer_size( p_sys->p_jack_client ); @@ -108,71 +138,102 @@ static int Open( vlc_object_t *p_this ) p_sys->i_channels = aout_FormatNbChannels( &p_aout->output.output ); + p_sys->p_jack_ports = malloc( p_sys->i_channels * + sizeof(jack_port_t *) ); + if( p_sys->p_jack_ports == NULL ) + { + status = VLC_ENOMEM; + goto error_out; + } + + p_sys->p_jack_buffers = malloc( p_sys->i_channels * + sizeof(jack_sample_t *) ); + if( p_sys->p_jack_buffers == NULL ) + { + status = VLC_ENOMEM; + goto error_out; + } + /* Create the output ports */ for( i = 0; i < p_sys->i_channels; i++ ) { - char p_name[32]; - snprintf( p_name, 32, "channel_%d", i + 1); - p_sys->p_jack_port[i] = jack_port_register( p_sys->p_jack_client, - p_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + snprintf( psz_name, sizeof(psz_name), "out_%d", i + 1); + psz_name[sizeof(psz_name) - 1] = '\0'; + p_sys->p_jack_ports[i] = jack_port_register( p_sys->p_jack_client, + psz_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); - if( p_sys->p_jack_port[i] == NULL ) + if( p_sys->p_jack_ports[i] == NULL ) { msg_Err( p_aout, "failed to register a JACK port" ); - jack_client_close( p_sys->p_jack_client ); - free( p_sys ); - return VLC_EGENERIC; + status = VLC_EGENERIC; + goto error_out; } } /* Tell the JACK server we are ready */ - if( jack_activate( p_sys->p_jack_client ) ) + i_error = jack_activate( p_sys->p_jack_client ); + if( i_error ) { - msg_Err( p_aout, "failed to activate JACK client" ); - jack_client_close( p_sys->p_jack_client ); - free( p_sys ); - return VLC_EGENERIC; + msg_Err( p_aout, "failed to activate JACK client (error %d)", i_error ); + status = VLC_EGENERIC; + goto error_out; } - - /* Find input ports to connect to */ - pp_in_ports = jack_get_ports( p_sys->p_jack_client, NULL, NULL, - JackPortIsInput ); - i_in_ports = 0; - while( pp_in_ports && pp_in_ports[i_in_ports] ) + /* Auto connect ports if we were asked to */ + if( config_GetInt( p_aout, AUTO_CONNECT_OPTION ) ) { - i_in_ports++; - } + unsigned int i_in_ports; + char *psz_regex = config_GetPsz( p_aout, CONNECT_REGEX_OPTION ); + const char **pp_in_ports = jack_get_ports( p_sys->p_jack_client, + psz_regex, NULL, + JackPortIsInput ); + free( psz_regex ); + /* Count the number of returned ports */ + i_in_ports = 0; + while( pp_in_ports && pp_in_ports[i_in_ports] ) + { + i_in_ports++; + } - /* Connect the output ports to input ports */ - if( i_in_ports > 0 ) - { - for( i = 0; i < p_sys->i_channels; i++ ) + /* Tie the output ports to JACK input ports */ + for( i = 0; i_in_ports > 0 && i < p_sys->i_channels; i++ ) { - int i_in = i % i_in_ports; - if( jack_connect( p_sys->p_jack_client, - jack_port_name( p_sys->p_jack_port[i] ), - pp_in_ports[i_in]) ) - { - msg_Err( p_aout, "failed to connect port %s to port %s", - jack_port_name( p_sys->p_jack_port[i] ), - pp_in_ports[i_in] ); + const char* psz_in = pp_in_ports[i % i_in_ports]; + const char* psz_out = jack_port_name( p_sys->p_jack_ports[i] ); + i_error = jack_connect( p_sys->p_jack_client, psz_out, psz_in ); + if( i_error ) + { + msg_Err( p_aout, "failed to connect port %s to port %s (error %d)", + psz_out, psz_in, i_error ); } else { msg_Dbg( p_aout, "connecting port %s to port %s", - jack_port_name( p_sys->p_jack_port[i] ), - pp_in_ports[i_in] ); + psz_out, psz_in ); } } + free( pp_in_ports ); } msg_Dbg( p_aout, "JACK audio output initialized (%d channels, buffer " "size=%d, rate=%d)", p_sys->i_channels, p_aout->output.i_nb_samples, p_aout->output.output.i_rate ); - return VLC_SUCCESS; +error_out: + /* Clean up, if an error occurred */ + if( status != VLC_SUCCESS && p_sys != NULL) + { + if( p_sys->p_jack_client ) + { + jack_deactivate( p_sys->p_jack_client ); + jack_client_close( p_sys->p_jack_client ); + } + free( p_sys->p_jack_ports ); + free( p_sys->p_jack_buffers ); + free( p_sys ); + } + return status; } @@ -181,36 +242,44 @@ static int Open( vlc_object_t *p_this ) *****************************************************************************/ int Process( jack_nframes_t i_frames, void *p_arg ) { - aout_buffer_t *p_buffer; - jack_default_audio_sample_t *p_jack_buffer; unsigned int i, j, i_nb_samples = 0; aout_instance_t *p_aout = (aout_instance_t*) p_arg; - unsigned int i_nb_channels = p_aout->output.p_sys->i_channels; + struct aout_sys_t *p_sys = p_aout->output.p_sys; + jack_sample_t *p_src = NULL; /* Get the next audio data buffer */ - p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); - - if( p_buffer ) + aout_buffer_t *p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); + if( p_buffer != NULL ) { + p_src = (jack_sample_t *)p_buffer->p_buffer; i_nb_samples = p_buffer->i_nb_samples; } - for( i = 0; i < i_nb_channels; i++ ) + /* Get the JACK buffers to write to */ + for( i = 0; i < p_sys->i_channels; i++ ) { - /* Get an output buffer from JACK */ - p_jack_buffer = jack_port_get_buffer( - p_aout->output.p_sys->p_jack_port[i], i_frames ); + p_sys->p_jack_buffers[i] = jack_port_get_buffer( p_sys->p_jack_ports[i], + i_frames ); + } - /* Fill the buffer with audio data */ - for( j = 0; j < i_nb_samples; j++ ) + /* Copy in the audio data */ + for( j = 0; j < i_nb_samples; j++ ) + { + for( i = 0; i < p_sys->i_channels; i++ ) { - p_jack_buffer[j] = ((float*)p_buffer->p_buffer)[i_nb_channels*j+i]; + jack_sample_t *p_dst = p_sys->p_jack_buffers[i]; + p_dst[j] = *p_src; + p_src++; } - if (i_nb_samples < i_frames) + } + + /* Fill any remaining buffer with silence */ + if( i_nb_samples < i_frames ) + { + for( i = 0; i < p_sys->i_channels; i++ ) { - memset( p_jack_buffer + i_nb_samples, 0, - sizeof( jack_default_audio_sample_t ) * - (i_frames - i_nb_samples) ); + memset( p_sys->p_jack_buffers[i] + i_nb_samples, 0, + sizeof( jack_sample_t ) * (i_frames - i_nb_samples) ); } } @@ -218,7 +287,6 @@ int Process( jack_nframes_t i_frames, void *p_arg ) { aout_BufferFree( p_buffer ); } - return 0; } @@ -236,10 +304,22 @@ static void Play( aout_instance_t *p_aout ) *****************************************************************************/ static void Close( vlc_object_t *p_this ) { + int i_error; aout_instance_t *p_aout = (aout_instance_t *)p_this; - struct aout_sys_t * p_sys = p_aout->output.p_sys; + struct aout_sys_t *p_sys = p_aout->output.p_sys; - jack_client_close( p_sys->p_jack_client ); + i_error = jack_deactivate( p_sys->p_jack_client ); + if( i_error ) + { + msg_Err( p_aout, "jack_deactivate failed (error %d)", i_error ); + } + + i_error = jack_client_close( p_sys->p_jack_client ); + if( i_error ) + { + msg_Err( p_aout, "jack_client_close failed (error %d)", i_error ); + } + free( p_sys->p_jack_ports ); + free( p_sys->p_jack_buffers ); free( p_sys ); } -