/* jack: audio output via JACK Audio Connection Kit copyright 2006 by the mpg123 project - free software under the terms of the LGPL 2.1 see COPYING and AUTHORS files in distribution or http://mpg123.org initially written by Nicholas J. Humfrey */ #include <math.h> #include <jack/jack.h> #include <jack/ringbuffer.h> #include <sys/errno.h> #include "mpg123app.h" #include "debug.h" #define MAX_CHANNELS (2) typedef struct { int channels; jack_port_t *ports[MAX_CHANNELS]; jack_ringbuffer_t * rb[MAX_CHANNELS]; size_t rb_size; jack_client_t *client; jack_default_audio_sample_t *tmp_buffer; } jack_handle_t, *jack_handle_ptr; static jack_handle_t* alloc_jack_handle() { jack_handle_t *handle=NULL; handle = malloc(sizeof(jack_handle_t)); if (!handle) { error("Failed to allocate memory for our handle."); return NULL; } /* Initialise the handle, and store for later*/ handle->rb[0] = NULL; handle->rb[1] = NULL; handle->ports[0] = NULL; handle->ports[1] = NULL; handle->client = NULL; handle->tmp_buffer = NULL; handle->rb_size = 0; return handle; } static void free_jack_handle( jack_handle_t* handle ) { int i; warning("FIXME: One needs to wait or write some silence here to prevent the last bits of audio to vanish out of the ringbuffer."); for(i=0; i<MAX_CHANNELS; i++) { /* Close the port for channel*/ if ( handle->ports[i] ) jack_port_unregister( handle->client, handle->ports[i] ); /* Free up the ring buffer for channel*/ if ( handle->rb[i] ) jack_ringbuffer_free( handle->rb[i] ); } if (handle->client) jack_client_close(handle->client); if (handle->tmp_buffer) free(handle->tmp_buffer); free(handle); } static int process_callback( jack_nframes_t nframes, void *arg ) { jack_handle_t* handle = (jack_handle_t*)arg; size_t to_read = sizeof (jack_default_audio_sample_t) * nframes; unsigned int c; /* copy data to ringbuffer; one per channel*/ for (c=0; c < handle->channels; c++) { char *buf = (char*)jack_port_get_buffer(handle->ports[c], nframes); size_t len = jack_ringbuffer_read(handle->rb[c], buf, to_read); /* If we don't have enough audio, fill it up with silence*/ /* (this is to deal with pausing etc.)*/ if (to_read > len) bzero( buf+len, to_read - len ); /*if (len < to_read)*/ /* error1("failed to read from ring buffer %d",c);*/ } /* Success*/ return 0; } static void shutdown_callback( void *arg ) { /* jack_handle_t* handle = (jack_handle_t*)arg; */ debug("shutdown_callback()"); } /* connect to jack ports named in the NULL-terminated wishlist */ static int real_connect_jack_ports( jack_handle_t* handle, const char** wishlist) { const char **wish = wishlist; int ch, err; if(wish != NULL && *wish == NULL) return 1; /* success, nothing connected as wanted */ for(ch=0; ch<handle->channels; ++ch) { const char* in = jack_port_name( handle->ports[ch] ); if ((err = jack_connect(handle->client, in, *wish)) != 0 && err != EEXIST) { error1("connect_jack_ports(): failed to jack_connect() ports: %d",err); return 0; } /* Increment wish only if there is another one. Otherwise simply connect to the same port multiple times. */ if(*(wish+1) != NULL) ++wish; } return 1; } /* crude way of automatically connecting up jack ports */ /* 0 on error */ static int autoconnect_jack_ports( jack_handle_t* handle) { const char **all_ports; unsigned int ch=0; int err,i; /* Get a list of all the jack ports*/ all_ports = jack_get_ports (handle->client, NULL, NULL, JackPortIsInput); if (!all_ports) { error("connect_jack_ports(): jack_get_ports() returned NULL."); return 0; } /* Step through each port name*/ for (i = 0; all_ports[i]; ++i) { const char* in = jack_port_name( handle->ports[ch] ); const char* out = all_ports[i]; if ((err = jack_connect(handle->client, in, out)) != 0 && err != EEXIST) { error1("connect_jack_ports(): failed to jack_connect() ports: %d",err); return 0; } /* Found enough ports ?*/ if (++ch >= handle->channels) break; } free( all_ports ); return 1; } static int connect_jack_ports( jack_handle_t* handle, const char *dev ) { if (dev==NULL || strcmp(dev, "auto")==0) { return autoconnect_jack_ports( handle ); } else { const char* wishlist[] = { NULL, NULL, NULL }; /* Two channels and end marker. */ char *devcopy; int ret; size_t len = strlen(dev); devcopy = malloc(len+1); if(devcopy == NULL){ error("OOM"); return 0; } /* We just look out for a possible second port, comma separated This is really crude cruft, but it's enough. Can be replaced by something sensible later. */ memcpy(devcopy, dev, len+1); if( len > 0 && strcmp(dev, "none")!=0 ) { size_t i=0; wishlist[0] = devcopy; while(devcopy[i] != 0 && devcopy[i] != ',') ++i; if(devcopy[i] == ',') { devcopy[i] = 0; wishlist[1] = devcopy+i+1; } } if(wishlist[0] == NULL) warning("Not connecting up jack ports as requested."); ret = real_connect_jack_ports(handle, wishlist); free(devcopy); return ret; } return 1; } static int close_jack(audio_output_t *ao) { jack_handle_t *handle = (jack_handle_t*)ao->userptr; debug("close_jack()."); /* Close and shutdown*/ if (handle) { free_jack_handle( handle ); ao->userptr = NULL; } return 0; } static int open_jack(audio_output_t *ao) { char client_name[255]; jack_handle_t *handle=NULL; jack_options_t jopt = JackNullOption; jack_status_t jstat = 0; unsigned int i; debug("jack open"); if(!ao) return -1; /* Return if already open*/ if (ao->userptr) { error("audio device is already open."); return -1; } /* Create some storage for ourselves*/ if((handle = alloc_jack_handle()) == NULL) return -1; ao->userptr = (void*)handle; /* Register with Jack*/ snprintf(client_name, 255, "mpg123-%d", getpid()); if ((handle->client = jack_client_open(client_name, jopt, &jstat)) == 0) { error1("Failed to open jack client: 0x%x", jstat); close_jack(ao); return -1; } /* Display the unique client name allocated to us */ fprintf(stderr,"Registered as JACK client %s.\n", jack_get_client_name( handle->client ) ); /* The initial open lets me choose the settings. */ if (ao->format==-1) { ao->format = MPG123_ENC_16; ao->rate = jack_get_sample_rate(handle->client); ao->channels = 2; } /* Check the sample rate is correct*/ if (jack_get_sample_rate( handle->client ) != (jack_nframes_t)ao->rate) { error("JACK Sample Rate is different to sample rate of file."); close_jack(ao); return -1; } /* Register ports with Jack*/ handle->channels = ao->channels; if (handle->channels == 1) { if (!(handle->ports[0] = jack_port_register(handle->client, "mono", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0))) { error("Cannot register JACK output port 'mono'."); close_jack(ao); return -1; } } else if (handle->channels == 2) { if (!(handle->ports[0] = jack_port_register(handle->client, "left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0))) { error("Cannot register JACK output port 'left'."); close_jack(ao); return -1; } if (!(handle->ports[1] = jack_port_register(handle->client, "right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0))) { error("Cannot register JACK output port 'right'."); close_jack(ao); return -1; } } else { error1("invalid number of output channels (%d).", handle->channels); close_jack(ao); return -1; } /* Create the ring buffers (one seconds audio)*/ handle->rb_size = jack_get_sample_rate(handle->client) * sizeof(jack_default_audio_sample_t); for(i=0;i<handle->channels;i++){ handle->rb[i]=jack_ringbuffer_create(handle->rb_size); } /* Set the callbacks*/ jack_set_process_callback(handle->client, process_callback, (void*)handle); jack_on_shutdown(handle->client, shutdown_callback, (void*)handle); /* Activate client*/ if (jack_activate(handle->client)) { error("Can't activate client."); } /* Connect up the portsm, return */ if(!connect_jack_ports( handle, ao->device )) { /* deregistering of ports will not work but should just fail, then, and let the rest clean up */ close_jack(ao); return -1; } debug("Jack open successful.\n"); return 0; } /* Jack prefers floats, I actually assume it does _only_ float/double (as it is nowadays)! */ static int get_formats_jack(audio_output_t *ao) { if(jack_get_sample_rate( ((jack_handle_t*)(ao->userptr))->client ) != (jack_nframes_t)ao->rate) return 0; else return MPG123_ENC_FLOAT_32|MPG123_ENC_FLOAT_64|MPG123_ENC_SIGNED_16; } static int write_jack(audio_output_t *ao, unsigned char *buf, int len) { int c,n = 0; jack_handle_t *handle = (jack_handle_t*)ao->userptr; jack_nframes_t samples; size_t tmp_size; /* Only float or double is used. Note: I still need the tmp buffer because of de-interleaving the channels. */ samples = len / (ao->format == MPG123_ENC_FLOAT_64 ? 8 : (ao->format == MPG123_ENC_SIGNED_16 ? 2 : 4)) / handle->channels; tmp_size = samples * sizeof( jack_default_audio_sample_t ); /* Sanity check that ring buffer is at least twice the size of the audio we just got*/ if (handle->rb_size/2 < len) { error("ring buffer is less than twice the size of audio given."); return -1; } /* Wait until there is space in the ring buffer*/ while (jack_ringbuffer_write_space( handle->rb[0] ) < tmp_size) { /* Sleep for a quarter of the ring buffer size (1/4 second)*/ usleep(250000); } /* Ensure the temporary buffer is big enough*/ handle->tmp_buffer = (jack_default_audio_sample_t*)realloc( handle->tmp_buffer, tmp_size); if (!handle->tmp_buffer) { error("failed to realloc temporary buffer."); return -1; } for(c=0; c<handle->channels; c++) { size_t len = 0; /* Hm, is that optimal? Anyhow, deinterleaving float or double ... or short. With/without conversion. */ if(ao->format == MPG123_ENC_SIGNED_16) { short* src = (short*)buf; for(n=0; n<samples; n++) handle->tmp_buffer[n] = src[(n*handle->channels)+c] / 32768.0f; } else if(ao->format == MPG123_ENC_FLOAT_32) { float* src = (float*)buf; for(n=0; n<samples; n++) handle->tmp_buffer[n] = src[(n*handle->channels)+c]; } else /* MPG123_ENC_FLOAT_64 */ { double* src = (double*)buf; for(n=0; n<samples; n++) handle->tmp_buffer[n] = src[(n*handle->channels)+c]; } /* Copy temporary buffer into ring buffer*/ len = jack_ringbuffer_write(handle->rb[c], (char*)handle->tmp_buffer, tmp_size); if (len < tmp_size) { error("failed to write to ring ruffer."); return -1; } } return len; } static void flush_jack(audio_output_t *ao) { jack_handle_t *handle = (jack_handle_t*)ao->userptr; int c; warning("This way, we drop audio on pausing... there must be a better way."); /* Reset the ring buffers*/ for(c=0; c<handle->channels; c++) { jack_ringbuffer_reset(handle->rb[c]); } } static int init_jack(audio_output_t* ao) { if (ao==NULL) return -1; /* Set callbacks */ ao->open = open_jack; ao->flush = flush_jack; ao->write = write_jack; ao->get_formats = get_formats_jack; ao->close = close_jack; /* Success */ return 0; } /* Module information data structure */ mpg123_module_t mpg123_output_module_info = { /* api_version */ MPG123_MODULE_API_VERSION, /* name */ "jack", /* description */ "Output audio using JACK (JACK Audio Connection Kit).", /* revision */ "$Rev:$", /* handle */ NULL, /* init_output */ init_jack, };