|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
jack: audio output via JACK Audio Connection Kit
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
copyright 2006-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
|
|
Packit |
c32a2d |
see COPYING and AUTHORS files in distribution or http://mpg123.org
|
|
Packit |
c32a2d |
initially written by Nicholas J. Humfrey
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
I reworked the processing logic. Only one ringbuffer, deinterleaving in the
|
|
Packit |
c32a2d |
processing callback. A semaphore to avoid using usleep() for waiting. Up
|
|
Packit |
c32a2d |
to 99 channels. Only float input (ensures that libmpg123 selects f32
|
|
Packit |
c32a2d |
encoding). This is still a hack to shoehorn the JACK API into our model.
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
Damn. I'm wary of he semaphore. I'm sure I constructed a deadlock there.
|
|
Packit |
c32a2d |
There's always a deadlock. --ThOr
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "out123_int.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include <math.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include <jack/jack.h>
|
|
Packit |
c32a2d |
#include <jack/ringbuffer.h>
|
|
Packit |
c32a2d |
/* Using some pthread to provide synchronization between process callback
|
|
Packit |
c32a2d |
and writer part. The JACK API is not meant for this. Libpthread is
|
|
Packit |
c32a2d |
pulled in as libjack dependency anyway. */
|
|
Packit |
c32a2d |
#include <semaphore.h>
|
|
Packit |
c32a2d |
#include <sys/errno.h>
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
#include "debug.h"
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
typedef struct {
|
|
Packit |
c32a2d |
int alive;
|
|
Packit |
c32a2d |
sem_t sem; /* semaphore to avoid busy waiting */
|
|
Packit |
c32a2d |
int channels;
|
|
Packit |
c32a2d |
int encoding;
|
|
Packit |
c32a2d |
int framesize;
|
|
Packit |
c32a2d |
jack_default_audio_sample_t **ports_buf;
|
|
Packit |
c32a2d |
jack_port_t **ports;
|
|
Packit |
c32a2d |
jack_ringbuffer_t *rb;
|
|
Packit |
c32a2d |
size_t rb_size; /* in bytes */
|
|
Packit |
c32a2d |
jack_client_t *client;
|
|
Packit |
c32a2d |
char *procbuf;
|
|
Packit |
c32a2d |
size_t procbuf_frames; /* in PCM frames */
|
|
Packit |
c32a2d |
} jack_handle_t, *jack_handle_ptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static jack_handle_t* alloc_jack_handle(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
jack_handle_t *handle=NULL;
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
handle = malloc(sizeof(jack_handle_t));
|
|
Packit |
c32a2d |
if (!handle)
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
handle->channels = ao->channels;
|
|
Packit |
c32a2d |
handle->encoding = ao->format;
|
|
Packit |
c32a2d |
handle->framesize = ao->framesize;
|
|
Packit |
c32a2d |
handle->rb = NULL;
|
|
Packit |
c32a2d |
handle->ports_buf = malloc( sizeof(jack_default_audio_sample_t*)
|
|
Packit |
c32a2d |
* ao->channels );
|
|
Packit |
c32a2d |
handle->ports = malloc(sizeof(jack_port_t*)*ao->channels);
|
|
Packit |
c32a2d |
if(!handle->ports_buf || !handle->ports)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(handle->ports_buf)
|
|
Packit |
c32a2d |
free(handle->ports_buf);
|
|
Packit |
c32a2d |
if(handle->ports)
|
|
Packit |
c32a2d |
free(handle->ports);
|
|
Packit |
c32a2d |
free(handle);
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
for(i=0; i<ao->channels; ++i)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
handle->ports_buf[i] = NULL;
|
|
Packit |
c32a2d |
handle->ports[i] = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(sem_init(&handle->sem, 0, 0))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("Semaphore init failed.");
|
|
Packit |
c32a2d |
free(handle->ports_buf);
|
|
Packit |
c32a2d |
free(handle->ports);
|
|
Packit |
c32a2d |
free(handle);
|
|
Packit |
c32a2d |
return NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
handle->alive = 0;
|
|
Packit |
c32a2d |
handle->client = NULL;
|
|
Packit |
c32a2d |
handle->procbuf = NULL;
|
|
Packit |
c32a2d |
handle->rb_size = 0;
|
|
Packit |
c32a2d |
handle->procbuf_frames = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return handle;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void free_jack_handle( jack_handle_t* handle )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int i;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(handle->ports)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(handle->client)
|
|
Packit |
c32a2d |
for(i=0; i<handle->channels; i++)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Close the port for channel*/
|
|
Packit |
c32a2d |
if(handle->ports[i])
|
|
Packit |
c32a2d |
jack_port_unregister(handle->client, handle->ports[i]);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
free(handle->ports);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(handle->ports_buf)
|
|
Packit |
c32a2d |
free(handle->ports_buf);
|
|
Packit |
c32a2d |
/* Free up the ring buffer for channel*/
|
|
Packit |
c32a2d |
if(handle->rb)
|
|
Packit |
c32a2d |
jack_ringbuffer_free(handle->rb);
|
|
Packit |
c32a2d |
if (handle->client)
|
|
Packit |
c32a2d |
jack_client_close(handle->client);
|
|
Packit |
c32a2d |
if (handle->procbuf)
|
|
Packit |
c32a2d |
free(handle->procbuf);
|
|
Packit |
c32a2d |
sem_destroy(&handle->sem);
|
|
Packit |
c32a2d |
free(handle);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int process_callback( jack_nframes_t nframes, void *arg )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
int c;
|
|
Packit |
c32a2d |
jack_handle_t* handle = (jack_handle_t*)arg;
|
|
Packit |
c32a2d |
size_t to_read = nframes;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
for(c=0; c<handle->channels; ++c)
|
|
Packit |
c32a2d |
handle->ports_buf[c] =
|
|
Packit |
c32a2d |
jack_port_get_buffer(handle->ports[c], nframes);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* One ringbuffer to rule them all, getting interleaved data piecewise
|
|
Packit |
c32a2d |
and appending to non-interleaved buffers. */
|
|
Packit |
c32a2d |
while(to_read)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Need to read into temporary storage, then deinterleave to JACK
|
|
Packit |
c32a2d |
buffers. */
|
|
Packit |
c32a2d |
size_t got_piece;
|
|
Packit |
c32a2d |
size_t avail_piece;
|
|
Packit |
c32a2d |
size_t piece = to_read > handle->procbuf_frames
|
|
Packit |
c32a2d |
? handle->procbuf_frames
|
|
Packit |
c32a2d |
: to_read;
|
|
Packit |
c32a2d |
/* Ensure we get only full PCM frames by checking available byte count
|
|
Packit |
c32a2d |
and reducing expectation. */
|
|
Packit |
c32a2d |
avail_piece = jack_ringbuffer_read_space(handle->rb)/handle->framesize;
|
|
Packit |
c32a2d |
got_piece = jack_ringbuffer_read( handle->rb
|
|
Packit |
c32a2d |
, handle->procbuf, (avail_piece > piece ? piece : avail_piece)
|
|
Packit |
c32a2d |
* handle->framesize ) / handle->framesize;
|
|
Packit |
c32a2d |
debug2( "fetched %"SIZE_P" frames from ringbuffer (wanted %"SIZE_P")"
|
|
Packit |
c32a2d |
, (size_p)got_piece, (size_p)piece );
|
|
Packit |
c32a2d |
/* If this is the last piece, fill up, not time to wait. */
|
|
Packit |
c32a2d |
if(to_read > piece)
|
|
Packit |
c32a2d |
piece = got_piece; /* We got further loop cycle(s) to get the rest. */
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(piece > got_piece)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug("filling up with zeros");
|
|
Packit |
c32a2d |
bzero( handle->procbuf+got_piece*handle->framesize
|
|
Packit |
c32a2d |
, (piece-got_piece)*handle->framesize );
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Now extract the pieces for the channels. */
|
|
Packit |
c32a2d |
for (c=0; c < handle->channels; ++c)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t n;
|
|
Packit |
c32a2d |
jack_default_audio_sample_t *dst = handle->ports_buf[c];
|
|
Packit |
c32a2d |
if(handle->encoding == MPG123_ENC_FLOAT_32)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
float* src = (float*)handle->procbuf;
|
|
Packit |
c32a2d |
for(n=0; n
|
|
Packit |
c32a2d |
*(dst++) = src[(n*handle->channels)+c];
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else /* MPG123_ENC_FLOAT_64 */
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
double* src = (double*)handle->procbuf;
|
|
Packit |
c32a2d |
for(n=0; n
|
|
Packit |
c32a2d |
*(dst++) = src[(n*handle->channels)+c];
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Store output buffer offset. */
|
|
Packit |
c32a2d |
handle->ports_buf[c] = dst;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Give the writer a hint about the time passed. */
|
|
Packit |
c32a2d |
sem_post(&handle->sem);
|
|
Packit |
c32a2d |
to_read -= piece;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Success*/
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* This is triggered on server shutdown and very much necessary to avoid
|
|
Packit |
c32a2d |
out123 hanging on the processor semaphore. */
|
|
Packit |
c32a2d |
static void shutdown_callback(void *arg)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
jack_handle_t* handle = (jack_handle_t*)arg;
|
|
Packit |
c32a2d |
handle->alive = 0;
|
|
Packit |
c32a2d |
sem_post(&handle->sem);
|
|
Packit |
c32a2d |
debug("shutdown_callback()");
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* connect to jack ports named in the NULL-terminated wishlist */
|
|
Packit |
c32a2d |
static int real_connect_jack_ports(out123_handle *ao
|
|
Packit |
c32a2d |
, jack_handle_t* handle, const char** wishlist)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char **wish = wishlist;
|
|
Packit |
c32a2d |
int ch, err;
|
|
Packit |
c32a2d |
int ch_wrap = 0, wish_wrap = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(!wish)
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
if(wish != NULL && *wish == NULL)
|
|
Packit |
c32a2d |
return 1; /* success, nothing connected as wanted */
|
|
Packit |
c32a2d |
ch=0;
|
|
Packit |
c32a2d |
/* Connect things as long as there are sources or sinks left. */
|
|
Packit |
c32a2d |
while(!wish_wrap || !ch_wrap)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char* in = jack_port_name(handle->ports[ch]);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if((err = jack_connect(handle->client, in, *wish)) != 0 && err != EEXIST)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error4( "connect_jack_ports(): failed to jack_connect() ch%i (%s) to %s: %d"
|
|
Packit |
c32a2d |
, ch, in ? in : "<nil>", *wish, err );
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
Increment channel and wishlist, both possibly wrapping around, to
|
|
Packit |
c32a2d |
ensure we connected all channels to some output port and provided
|
|
Packit |
c32a2d |
some input to all ports in the wishlist. Both cases of less channels
|
|
Packit |
c32a2d |
than output ports (splitting) and more channels than output ports
|
|
Packit |
c32a2d |
(downmix) are sensible.
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
if(++ch == handle->channels)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
ch = 0;
|
|
Packit |
c32a2d |
++ch_wrap;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(!*(++wish))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
wish = wishlist;
|
|
Packit |
c32a2d |
++wish_wrap;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* crude way of automatically connecting up jack ports */
|
|
Packit |
c32a2d |
/* 0 on error */
|
|
Packit |
c32a2d |
static int autoconnect_jack_ports(out123_handle *ao, jack_handle_t* handle)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char **all_ports;
|
|
Packit |
c32a2d |
unsigned int ch=0;
|
|
Packit |
c32a2d |
int err,i;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Get a list of all the jack ports*/
|
|
Packit |
c32a2d |
all_ports = jack_get_ports (handle->client, NULL, NULL, JackPortIsInput);
|
|
Packit |
c32a2d |
if(!all_ports)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("connect_jack_ports(): jack_get_ports() returned NULL.");
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Step through each port name*/
|
|
Packit |
c32a2d |
for (i = 0; all_ports[i]; ++i)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
const char* in = jack_port_name( handle->ports[ch] );
|
|
Packit |
c32a2d |
const char* out = all_ports[i];
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if ((err = jack_connect(handle->client, in, out)) != 0 && err != EEXIST)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error1("connect_jack_ports(): failed to jack_connect() ports: %d",err);
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Found enough ports ?*/
|
|
Packit |
c32a2d |
if (++ch >= handle->channels) break;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
jack_free(all_ports);
|
|
Packit |
c32a2d |
return 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int connect_jack_ports(out123_handle *ao
|
|
Packit |
c32a2d |
, jack_handle_t* handle)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug1("connect_jack_ports with dev=%s", ao->device ? ao->device : "<nil>");
|
|
Packit |
c32a2d |
if(ao->device==NULL || strcmp(ao->device, "auto")==0)
|
|
Packit |
c32a2d |
return autoconnect_jack_ports(ao, handle);
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Parse device for a set of ports, comma separated. */
|
|
Packit |
c32a2d |
const char** wishlist; /* Channels and end marker. */
|
|
Packit |
c32a2d |
int wish_channels = 1; /* Numper of entries in wishlist. */
|
|
Packit |
c32a2d |
char *devcopy, *chr;
|
|
Packit |
c32a2d |
int ret;
|
|
Packit |
c32a2d |
int c;
|
|
Packit |
c32a2d |
size_t len;
|
|
Packit |
c32a2d |
len = strlen(ao->device);
|
|
Packit |
c32a2d |
/* We connect as many JACK ports as desired, possibly duplicating. */
|
|
Packit |
c32a2d |
for(chr=ao->device; *chr; ++chr)
|
|
Packit |
c32a2d |
if(*chr == ',')
|
|
Packit |
c32a2d |
++wish_channels;
|
|
Packit |
c32a2d |
debug1("wish_channels: %i", wish_channels);
|
|
Packit |
c32a2d |
wishlist = malloc(sizeof(char*)*(wish_channels+1));
|
|
Packit |
c32a2d |
devcopy = compat_strdup(ao->device);
|
|
Packit |
c32a2d |
if(devcopy == NULL || wishlist == NULL)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(devcopy)
|
|
Packit |
c32a2d |
free(devcopy);
|
|
Packit |
c32a2d |
if(wishlist)
|
|
Packit |
c32a2d |
free(wishlist);
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("OOM");
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
for(c=0;c<=wish_channels;++c)
|
|
Packit |
c32a2d |
wishlist[c] = NULL;
|
|
Packit |
c32a2d |
if(len && strcmp(devcopy, "none"))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t i=0;
|
|
Packit |
c32a2d |
wishlist[0] = devcopy;
|
|
Packit |
c32a2d |
for(c=0;c
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
while(devcopy[i] != 0 && devcopy[i] != ',') ++i;
|
|
Packit |
c32a2d |
debug2("devcopy[%"SIZE_P"]=%i", i, devcopy[i]);
|
|
Packit |
c32a2d |
if(devcopy[i] == ',')
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* Terminate previous port name, assign next one. */
|
|
Packit |
c32a2d |
devcopy[i++] = 0;
|
|
Packit |
c32a2d |
debug2("terminaled wish %i: %s", c, wishlist[c]);
|
|
Packit |
c32a2d |
if(c+1 < wish_channels)
|
|
Packit |
c32a2d |
wishlist[c+1] = devcopy+i;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
if(wishlist[0] == NULL && !AOQUIET)
|
|
Packit |
c32a2d |
warning("Not connecting up jack ports as requested.");
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
ret = real_connect_jack_ports(ao, handle, wishlist);
|
|
Packit |
c32a2d |
free(devcopy);
|
|
Packit |
c32a2d |
free(wishlist);
|
|
Packit |
c32a2d |
return ret;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
return 1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void drain_jack(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug("drain_jack().");
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
do errno = 0;
|
|
Packit |
c32a2d |
while(sem_trywait(&handle->sem) == 0 || errno == EINTR);
|
|
Packit |
c32a2d |
/* For some reason, a single byte is reserved by JACK?! */
|
|
Packit |
c32a2d |
while( handle && handle->alive && handle->rb
|
|
Packit |
c32a2d |
&& jack_ringbuffer_write_space(handle->rb)+1 < handle->rb_size )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
debug2( "JACK close wait %"SIZE_P" < %"SIZE_P"\n"
|
|
Packit |
c32a2d |
, (size_p)jack_ringbuffer_write_space(handle->rb)
|
|
Packit |
c32a2d |
, (size_p)handle->rb_size );
|
|
Packit |
c32a2d |
sem_wait(&handle->sem);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int close_jack(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug("close_jack().");
|
|
Packit |
c32a2d |
/* Close and shutdown*/
|
|
Packit |
c32a2d |
if(handle)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
free_jack_handle(handle);
|
|
Packit |
c32a2d |
ao->userptr = NULL;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int open_jack(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
jack_handle_t *handle=NULL;
|
|
Packit |
c32a2d |
jack_options_t jopt = JackNullOption|JackNoStartServer;
|
|
Packit |
c32a2d |
jack_status_t jstat = 0;
|
|
Packit |
c32a2d |
unsigned int i;
|
|
Packit |
c32a2d |
char *realname;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug("jack open");
|
|
Packit |
c32a2d |
if(!ao)
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Return if already open*/
|
|
Packit |
c32a2d |
if(ao->userptr)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("audio device is already open.");
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* The initial open lets me choose the settings. */
|
|
Packit |
c32a2d |
if (ao->format==-1)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
ao->format = MPG123_ENC_FLOAT_32;
|
|
Packit |
c32a2d |
ao->channels = 2;
|
|
Packit |
c32a2d |
/* Really need a framesize defined for callback. */
|
|
Packit |
c32a2d |
ao->framesize = 2*4;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else if(!(ao->format & MPG123_ENC_FLOAT))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("JACK only wants float!");
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Create some storage for ourselves*/
|
|
Packit |
c32a2d |
if((handle = alloc_jack_handle(ao)) == NULL)
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
ao->userptr = (void*)handle;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Register with Jack*/
|
|
Packit |
c32a2d |
if((handle->client = jack_client_open(ao->name, jopt, &jstat)) == 0)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error1("Failed to open jack client: 0x%x", jstat);
|
|
Packit |
c32a2d |
close_jack(ao);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
realname = jack_get_client_name(handle->client);
|
|
Packit |
c32a2d |
/* Display the unique client name allocated to us */
|
|
Packit |
c32a2d |
if(AOVERBOSE(1))
|
|
Packit |
c32a2d |
fprintf( stderr, "Registered as JACK client %s.\n"
|
|
Packit |
c32a2d |
, realname ? realname : "<nil>" );
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Just make sure. */
|
|
Packit |
c32a2d |
ao->rate = jack_get_sample_rate(handle->client);
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Check the sample rate is correct*/
|
|
Packit |
c32a2d |
if (jack_get_sample_rate( handle->client ) != (jack_nframes_t)ao->rate)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("JACK Sample Rate is different to sample rate of file.");
|
|
Packit |
c32a2d |
close_jack(ao);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Register ports with Jack*/
|
|
Packit |
c32a2d |
if(handle->channels > 0 && handle->channels < 100)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
for(i=0;i<handle->channels;++i)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
char numbuf[3]; /* two digits, zero byte */
|
|
Packit |
c32a2d |
sprintf(numbuf, "%d", i+1);
|
|
Packit |
c32a2d |
if( !(handle->ports[i] = jack_port_register( handle->client
|
|
Packit |
c32a2d |
, numbuf, JACK_DEFAULT_AUDIO_TYPE
|
|
Packit |
c32a2d |
, JackPortIsOutput, 0 )) )
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error1("Cannot register JACK output port '%s'.", numbuf);
|
|
Packit |
c32a2d |
close_jack(ao);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error1("excessive number of output channels (%d).", handle->channels);
|
|
Packit |
c32a2d |
close_jack(ao);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Use device_buffer parameter for ring buffer, but ensure that two
|
|
Packit |
c32a2d |
JACK buffers fit in there. We do not support that buffer increasing
|
|
Packit |
c32a2d |
later on. */
|
|
Packit |
c32a2d |
handle->rb_size = (size_t)( ao->device_buffer
|
|
Packit |
c32a2d |
* jack_get_sample_rate(handle->client)
|
|
Packit |
c32a2d |
+ 0.5 ); /* PCM frames */
|
|
Packit |
c32a2d |
handle->procbuf_frames = jack_get_buffer_size(handle->client);
|
|
Packit |
c32a2d |
if(handle->rb_size < 2*handle->procbuf_frames)
|
|
Packit |
c32a2d |
handle->rb_size = 2*handle->procbuf_frames;
|
|
Packit |
c32a2d |
debug1("JACK ringbuffer for %"SIZE_P" PCM frames", (size_p)handle->rb_size);
|
|
Packit |
c32a2d |
/* Convert to bytes. */
|
|
Packit |
c32a2d |
handle->rb_size *= handle->framesize;
|
|
Packit |
c32a2d |
handle->rb = jack_ringbuffer_create(handle->rb_size);
|
|
Packit |
c32a2d |
handle->procbuf = malloc(handle->procbuf_frames*handle->framesize);
|
|
Packit |
c32a2d |
if(!handle->rb || !handle->procbuf)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("failed to allocate buffers");
|
|
Packit |
c32a2d |
close_jack(ao);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Set the callbacks*/
|
|
Packit |
c32a2d |
jack_set_process_callback(handle->client, process_callback, (void*)handle);
|
|
Packit |
c32a2d |
jack_on_shutdown(handle->client, shutdown_callback, (void*)handle);
|
|
Packit |
c32a2d |
handle->alive = 1;
|
|
Packit |
c32a2d |
/* Activate client*/
|
|
Packit |
c32a2d |
if(jack_activate(handle->client))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("Can't activate client.");
|
|
Packit |
c32a2d |
close_jack(ao);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Connect up the portsm, return */
|
|
Packit |
c32a2d |
if(!connect_jack_ports(ao, handle))
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
/* deregistering of ports will not work but should just fail, then,
|
|
Packit |
c32a2d |
and let the rest clean up */
|
|
Packit |
c32a2d |
close_jack(ao);
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug("Jack open successful.\n");
|
|
Packit |
c32a2d |
ao->realname = compat_strdup(realname);
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* Jack prefers floats, I actually assume it does _only_ float/double
|
|
Packit |
c32a2d |
(as it is nowadays)! */
|
|
Packit |
c32a2d |
static int get_formats_jack(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
if(jack_get_sample_rate(handle->client) != (jack_nframes_t)ao->rate)
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
return MPG123_ENC_FLOAT_32|MPG123_ENC_FLOAT_64;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int write_jack(out123_handle *ao, unsigned char *buf, int len)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
|
|
Packit |
c32a2d |
size_t bytes_left;
|
|
Packit |
c32a2d |
unsigned int strike = 0;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
bytes_left = len;
|
|
Packit |
c32a2d |
while(bytes_left && handle->alive)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
size_t piece;
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
debug("writing to ringbuffer");
|
|
Packit |
c32a2d |
/* No help: piece1 = jack_ringbuffer_write_space(handle->rb); */
|
|
Packit |
c32a2d |
piece = jack_ringbuffer_write(handle->rb, (char*)buf, bytes_left);
|
|
Packit |
c32a2d |
debug1("wrote %"SIZE_P" B", (size_p)piece);
|
|
Packit |
c32a2d |
buf += piece;
|
|
Packit |
c32a2d |
bytes_left -= piece;
|
|
Packit |
c32a2d |
/* Allow nothing being written some times, but not too often.
|
|
Packit |
c32a2d |
Don't know how often in a row that would be supposed to happen. */
|
|
Packit |
c32a2d |
if(!piece)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(++strike > 100)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if(!AOQUIET)
|
|
Packit |
c32a2d |
error("Cannot write to ringbuffer.");
|
|
Packit |
c32a2d |
break;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
/* Avoid busy waiting and semaphore accumulation:
|
|
Packit |
c32a2d |
Wait once on the semaphore, then clear it. We count on it being
|
|
Packit |
c32a2d |
posted by the process callback and we are going to push new data
|
|
Packit |
c32a2d |
so that that one gets the chance. */
|
|
Packit |
c32a2d |
sem_wait(&handle->sem);
|
|
Packit |
c32a2d |
do errno = 0;
|
|
Packit |
c32a2d |
while(sem_trywait(&handle->sem) == 0 || errno == EINTR);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
else
|
|
Packit |
c32a2d |
strike = 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
return len-bytes_left;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static void flush_jack(out123_handle *ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
jack_handle_t *handle = (jack_handle_t*)ao->userptr;
|
|
Packit |
c32a2d |
/* Reset the ring buffers*/
|
|
Packit |
c32a2d |
jack_ringbuffer_reset(handle->rb);
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
static int init_jack(out123_handle* ao)
|
|
Packit |
c32a2d |
{
|
|
Packit |
c32a2d |
if (ao==NULL)
|
|
Packit |
c32a2d |
return -1;
|
|
Packit |
c32a2d |
/* Set callbacks */
|
|
Packit |
c32a2d |
ao->open = open_jack;
|
|
Packit |
c32a2d |
ao->flush = flush_jack;
|
|
Packit |
c32a2d |
ao->drain = drain_jack;
|
|
Packit |
c32a2d |
ao->write = write_jack;
|
|
Packit |
c32a2d |
ao->get_formats = get_formats_jack;
|
|
Packit |
c32a2d |
ao->close = close_jack;
|
|
Packit |
c32a2d |
ao->propflags |= OUT123_PROP_PERSISTENT;
|
|
Packit |
c32a2d |
/* Success */
|
|
Packit |
c32a2d |
return 0;
|
|
Packit |
c32a2d |
}
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/*
|
|
Packit |
c32a2d |
Module information data structure
|
|
Packit |
c32a2d |
*/
|
|
Packit |
c32a2d |
mpg123_module_t mpg123_output_module_info = {
|
|
Packit |
c32a2d |
/* api_version */ MPG123_MODULE_API_VERSION,
|
|
Packit |
c32a2d |
/* name */ "jack",
|
|
Packit |
c32a2d |
/* description */ "Output audio using JACK (JACK Audio Connection Kit).",
|
|
Packit |
c32a2d |
/* revision */ "$Rev:$",
|
|
Packit |
c32a2d |
/* handle */ NULL,
|
|
Packit |
c32a2d |
|
|
Packit |
c32a2d |
/* init_output */ init_jack
|
|
Packit |
c32a2d |
};
|