698 lines
22 KiB
C
698 lines
22 KiB
C
#include "jack_backend.h"
|
|
#include <jack/jack.h>
|
|
#include <jack/ringbuffer.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
//#include <pthread.h>
|
|
#include "../logger.h"
|
|
|
|
#define NB_BUFFERS 2
|
|
|
|
struct jack_backend_t
|
|
{
|
|
struct audio_backend_t parent;
|
|
jack_client_t* jack_client;
|
|
jack_port_t* ports[VBAN_CHANNELS_MAX_NB];
|
|
jack_ringbuffer_t* ring_buffer;
|
|
size_t buffer_size;
|
|
enum VBanBitResolution bit_fmt;
|
|
unsigned int nb_channels;
|
|
unsigned char map[VBAN_CHANNELS_MAX_NB];
|
|
enum audio_direction direction;
|
|
uint32_t autoconnect;
|
|
int active;
|
|
};
|
|
long overruns = 0;
|
|
|
|
static int jack_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config);
|
|
static int jack_close(audio_backend_handle_t handle);
|
|
static int jack_write(audio_backend_handle_t handle, char const* data, size_t nb_sample);
|
|
static int jack_read(audio_backend_handle_t handle, char* data, size_t nb_sample);
|
|
static int jack_process_cb_tx(jack_nframes_t nframes, void* arg);
|
|
static int jack_process_cb_rx(jack_nframes_t nframes, void* arg);
|
|
static void jack_shutdown_cb(void* arg);
|
|
static jack_default_audio_sample_t jack_convert_sample_rx(char const* ptr, enum VBanBitResolution bit_fmt);
|
|
static void jack_convert_sample_tx(char* ptr, jack_default_audio_sample_t sample, enum VBanBitResolution bit_fmt);
|
|
pthread_mutex_t tx_read_thread_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
pthread_cond_t data_ready = PTHREAD_COND_INITIALIZER;
|
|
|
|
|
|
static int jack_start(struct jack_backend_t* jack_backend)
|
|
{
|
|
int ret = 0;
|
|
size_t port;
|
|
char port_name[32];
|
|
char port_name_template[32] = { 0 };
|
|
char const** ports;
|
|
volatile size_t port_id;
|
|
volatile size_t pports = 0;
|
|
uint8_t JackFlags;
|
|
|
|
memset(port_name_template, 0, 9);
|
|
|
|
switch (jack_backend->direction)
|
|
{
|
|
case AUDIO_IN:
|
|
if (jack_backend->autoconnect == CARD)
|
|
{
|
|
memcpy(port_name_template, "playback_%u", sizeof(port_name_template)-1);
|
|
JackFlags = JackPortIsInput + JackPortIsPhysical;
|
|
}
|
|
else
|
|
{
|
|
memcpy(port_name_template, "input_%u", sizeof(port_name_template)-1);
|
|
JackFlags = JackPortIsInput;
|
|
}
|
|
|
|
for (port = 0; port != jack_backend->nb_channels; ++port)
|
|
{
|
|
snprintf(port_name, sizeof(port_name)-1, port_name_template, (unsigned int)(port+1));
|
|
jack_backend->ports[port] = jack_port_register(jack_backend->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackFlags, 0);
|
|
if (jack_backend->ports[port] == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: impossible to set jack port for channel %d", __func__, port);
|
|
jack_close((audio_backend_handle_t)jack_backend);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
break;
|
|
default: // AUDIO_OUT:
|
|
if (jack_backend->autoconnect == CARD)
|
|
{
|
|
memcpy(port_name_template, "capture_%u", sizeof(port_name_template)-1);
|
|
JackFlags = JackPortIsOutput + JackPortIsPhysical;
|
|
}
|
|
else
|
|
{
|
|
memcpy(port_name_template, "output_%u", sizeof(port_name_template)-1);
|
|
JackFlags = JackPortIsOutput;
|
|
}
|
|
|
|
for (port = 0; port != jack_backend->nb_channels; ++port)
|
|
{
|
|
snprintf(port_name, sizeof(port_name)-1, port_name_template, (unsigned int)(port+1));
|
|
jack_backend->ports[port] = jack_port_register(jack_backend->jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackFlags, 0);
|
|
if (jack_backend->ports[port] == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: impossible to set jack port for channel %d", __func__, port);
|
|
jack_close((audio_backend_handle_t)jack_backend);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
ret = jack_activate(jack_backend->jack_client);
|
|
if (ret)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: can't activate client", __func__);
|
|
return ret;
|
|
}
|
|
|
|
if (jack_backend->autoconnect == YES)
|
|
{
|
|
switch (jack_backend->direction)
|
|
{
|
|
case AUDIO_IN:
|
|
ports = jack_get_ports(jack_backend->jack_client, 0, 0, JackPortIsPhysical|JackPortIsOutput);
|
|
|
|
if (ports != 0)
|
|
{
|
|
for (port_id=0; port_id<1024; port_id++)
|
|
{
|
|
if (ports[port_id]==0) break;
|
|
if (strstr(ports[port_id], "midi")==NULL) pports++;
|
|
}
|
|
for (port_id=0; port_id<jack_backend->nb_channels; port_id++)
|
|
{
|
|
if (jack_backend->map[port_id]<=pports)
|
|
{
|
|
if (ports[jack_backend->map[port_id]]!=NULL) if (strstr(ports[jack_backend->map[port_id]], "midi")==NULL)
|
|
{
|
|
ret = jack_connect(jack_backend->jack_client, ports[jack_backend->map[port_id]], jack_port_name(jack_backend->ports[port_id]));
|
|
if (ret)
|
|
{
|
|
logger_log(LOG_WARNING, "%s: could not autoconnect channel %d", __func__, port_id);
|
|
}
|
|
else
|
|
{
|
|
logger_log(LOG_DEBUG, "%s: channel %d autoconnected", __func__, port_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
jack_free(ports);
|
|
}
|
|
else
|
|
{
|
|
logger_log(LOG_WARNING, "%s: could not autoconnect channels", __func__);
|
|
}
|
|
break;
|
|
default: // AUDIO_OUT
|
|
ports = jack_get_ports(jack_backend->jack_client, 0, 0, JackPortIsPhysical|JackPortIsInput);
|
|
|
|
if (ports != 0)
|
|
{
|
|
for (port_id=0; port_id<1024; port_id++)
|
|
{
|
|
if (ports[port_id]==0) break;
|
|
if (strstr(ports[port_id], "midi")==NULL) pports++;
|
|
}
|
|
|
|
for (port_id=0; port_id<jack_backend->nb_channels; port_id++)
|
|
{
|
|
if (jack_backend->map[port_id]<=pports)
|
|
{
|
|
if (strstr(ports[jack_backend->map[port_id]], "midi")==NULL)
|
|
{
|
|
ret = jack_connect(jack_backend->jack_client, jack_port_name(jack_backend->ports[port_id]), ports[jack_backend->map[port_id]]);
|
|
if (ret)
|
|
{
|
|
logger_log(LOG_WARNING, "%s: could not autoconnect channel %d", __func__, port_id);
|
|
}
|
|
else
|
|
{
|
|
logger_log(LOG_DEBUG, "%s: channel %d autoconnected", __func__, port_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
jack_free(ports);
|
|
}
|
|
else
|
|
{
|
|
logger_log(LOG_WARNING, "%s: could not autoconnect channels", __func__);
|
|
}
|
|
break;
|
|
}//*/
|
|
}
|
|
|
|
logger_log(LOG_DEBUG, "%s: jack activated", __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int jack_backend_init(audio_backend_handle_t* handle)
|
|
{
|
|
struct jack_backend_t* jack_backend = 0;
|
|
|
|
if (handle == 0)
|
|
{
|
|
logger_log(LOG_FATAL, "%s: null handle pointer", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
jack_backend = calloc(1, sizeof(struct jack_backend_t));
|
|
if (jack_backend == 0)
|
|
{
|
|
logger_log(LOG_FATAL, "%s: could not allocate memory", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
jack_backend->parent.open = jack_open;
|
|
jack_backend->parent.close = jack_close;
|
|
jack_backend->parent.write = jack_write;
|
|
jack_backend->parent.read = jack_read;
|
|
|
|
*handle = (audio_backend_handle_t)jack_backend;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int jack_open(audio_backend_handle_t handle, char const* output_name, enum audio_direction direction, size_t buffer_size, struct stream_config_t const* config)
|
|
{
|
|
int ret;
|
|
uint32_t jbs, res;
|
|
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)handle;
|
|
volatile jack_nframes_t jack_buffer_size;
|
|
|
|
jack_backend->direction = direction;
|
|
jack_backend->autoconnect = config->autoconnect;
|
|
jack_backend->nb_channels = config->nb_channels;
|
|
jack_backend->bit_fmt = config->bit_fmt;
|
|
memcpy(jack_backend->map, config->map, VBAN_CHANNELS_MAX_NB);
|
|
|
|
char name[24];
|
|
memset(name, 0, 16);
|
|
|
|
switch (jack_backend->direction)
|
|
{
|
|
case AUDIO_IN:
|
|
strncpy(name, "VBAN TX ", 8);
|
|
break;
|
|
default: // AUDIO_OUT
|
|
strncpy(name, "VBAN RX ", 8);
|
|
break;
|
|
}
|
|
|
|
strcat(name, config->streamname);
|
|
|
|
logger_log(LOG_DEBUG, "%s", __func__);
|
|
|
|
if (handle == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: handle is null", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (jack_backend->jack_client == 0)
|
|
{
|
|
//jack_backend->jack_client = jack_client_open((output_name[0] == '\0') ? name : output_name, 0, 0);
|
|
jack_backend->jack_client = jack_client_open(name, JackNullOption, NULL);
|
|
if (jack_backend->jack_client == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: could not open jack client", __func__);
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
jack_buffer_size = jack_get_buffer_size(jack_backend->jack_client) * jack_backend->nb_channels * VBanBitResolutionSize[config->bit_fmt];
|
|
buffer_size = ((buffer_size > jack_buffer_size) ? buffer_size : jack_buffer_size) * NB_BUFFERS;
|
|
jack_backend->ring_buffer = jack_ringbuffer_create(buffer_size);
|
|
/*char* const zeros = calloc(1, buffer_size / NB_BUFFERS);
|
|
jack_ringbuffer_write(jack_backend->ring_buffer, zeros, buffer_size / NB_BUFFERS);
|
|
free(zeros);//*/
|
|
|
|
switch (jack_backend->direction)
|
|
{
|
|
case AUDIO_IN:
|
|
ret = jack_set_process_callback(jack_backend->jack_client, jack_process_cb_tx, jack_backend);
|
|
break;
|
|
default: // AUDIO_OUT
|
|
ret = jack_set_process_callback(jack_backend->jack_client, jack_process_cb_rx, jack_backend);
|
|
break;
|
|
}
|
|
|
|
if (ret)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: impossible to set jack process callback", __func__);
|
|
jack_close(handle);
|
|
return ret;
|
|
}
|
|
|
|
jack_on_shutdown(jack_backend->jack_client, jack_shutdown_cb, jack_backend);
|
|
|
|
ret = jack_start(jack_backend);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int jack_close(audio_backend_handle_t handle)
|
|
{
|
|
int ret = 0;
|
|
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)handle;
|
|
|
|
if (handle == 0)
|
|
{
|
|
logger_log(LOG_FATAL, "%s: handle pointer is null", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (jack_backend->jack_client == 0)
|
|
{
|
|
/** nothing to do */
|
|
return 0;
|
|
}
|
|
|
|
jack_backend->active = 0;
|
|
|
|
ret = jack_deactivate(jack_backend->jack_client);
|
|
if (ret)
|
|
{
|
|
logger_log(LOG_FATAL, "%s: jack_deactivate failed with error %d", __func__);
|
|
}
|
|
|
|
ret = jack_client_close(jack_backend->jack_client);
|
|
if (ret)
|
|
{
|
|
logger_log(LOG_FATAL, "%s: jack_client_close failed with error %d", __func__);
|
|
}
|
|
|
|
if (jack_backend->ring_buffer != 0)
|
|
{
|
|
jack_ringbuffer_free(jack_backend->ring_buffer);
|
|
}
|
|
|
|
jack_backend->jack_client = 0;
|
|
memset(jack_backend->ports, 0, VBAN_CHANNELS_MAX_NB * sizeof(jack_port_t*));
|
|
|
|
return ret;
|
|
}
|
|
|
|
int jack_write(audio_backend_handle_t handle, char const* data, size_t size)
|
|
{
|
|
int ret = 0;
|
|
size_t available;
|
|
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)handle;
|
|
|
|
logger_log(LOG_DEBUG, "%s", __func__);
|
|
|
|
if ((handle == 0) || (data == 0))
|
|
{
|
|
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (jack_backend->jack_client == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: device not open", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (jack_backend->active == 0)
|
|
{
|
|
logger_log(LOG_DEBUG, "%s: server not active yet", __func__);
|
|
return size;
|
|
}
|
|
|
|
if (jack_ringbuffer_write_space(jack_backend->ring_buffer) < size)
|
|
{
|
|
logger_log(LOG_WARNING, "%s: short write", __func__);
|
|
return 0;
|
|
}
|
|
|
|
//available = jack_ringbuffer_write_space(jack_backend->ring_buffer);
|
|
//if (available>size) available = size;
|
|
//else available = (available/(VBanBitResolutionSize[jack_backend->bit_fmt]*jack_backend->nb_channels))*VBanBitResolutionSize[jack_backend->bit_fmt]*jack_backend->nb_channels;
|
|
//jack_ringbuffer_write(jack_backend->ring_buffer, data, available);
|
|
while (jack_ringbuffer_write_space(jack_backend->ring_buffer)<size);
|
|
jack_ringbuffer_write(jack_backend->ring_buffer, data, size);
|
|
|
|
return (ret < 0) ? ret : size;
|
|
}
|
|
|
|
static int jack_read(audio_backend_handle_t handle, char* data, size_t size)
|
|
{
|
|
int ret = 0;
|
|
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)handle;
|
|
volatile size_t jsize;
|
|
//size_t bufSize = jack_backend->nb_channels * ;
|
|
|
|
logger_log(LOG_DEBUG, "%s", __func__);
|
|
|
|
if ((handle == 0) || (data == 0))
|
|
{
|
|
logger_log(LOG_ERROR, "%s: handle or data pointer is null", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (jack_backend->jack_client == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: device not open", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (jack_backend->active == 0)
|
|
{
|
|
logger_log(LOG_DEBUG, "%s: server not active yet", __func__);
|
|
return size;
|
|
}
|
|
|
|
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
|
pthread_mutex_lock (&tx_read_thread_lock);
|
|
while (jack_ringbuffer_read_space(jack_backend->ring_buffer)<size) pthread_cond_wait(&data_ready, &tx_read_thread_lock);
|
|
pthread_mutex_unlock(&tx_read_thread_lock);
|
|
jack_ringbuffer_read(jack_backend->ring_buffer, data, size);
|
|
|
|
return (ret < 0) ? ret : size;
|
|
}
|
|
|
|
int jack_process_cb_tx(jack_nframes_t nframes, void* arg)
|
|
{
|
|
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)arg;
|
|
size_t channel;
|
|
size_t sample;
|
|
jack_ringbuffer_data_t rb_data[2];
|
|
static jack_default_audio_sample_t* buffers[VBAN_CHANNELS_MAX_NB];
|
|
/* char* ptr;
|
|
size_t len;
|
|
size_t index = 0;
|
|
size_t part;*/
|
|
size_t sampleSize;
|
|
size_t bufSize; // size of buffer for ALL channels
|
|
char sampleParts[8];
|
|
|
|
if (arg == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: handle pointer is null", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
logger_log(LOG_DEBUG, "%s", __func__);
|
|
|
|
sampleSize = VBanBitResolutionSize[jack_backend->bit_fmt];
|
|
bufSize = (nframes * jack_backend->nb_channels * sampleSize);
|
|
jack_backend->active = 1;
|
|
|
|
for (channel = 0; channel < jack_backend->nb_channels; channel++)
|
|
{
|
|
buffers[channel] = (jack_default_audio_sample_t*)jack_port_get_buffer(jack_backend->ports[channel], nframes);
|
|
}
|
|
|
|
jack_ringbuffer_get_write_vector(jack_backend->ring_buffer, rb_data);
|
|
|
|
if ((rb_data[0].len + rb_data[1].len) < bufSize)
|
|
{
|
|
logger_log(LOG_WARNING, "%s: short read", __func__);
|
|
//return 0;
|
|
}
|
|
|
|
// Attempt to mirror the callback process from the receiver's one.
|
|
// Unfinished. Here's segfaults.
|
|
|
|
/*ptr = rb_data[0].buf;
|
|
len = 0;
|
|
for (sample = 0; sample != nframes; ++sample)
|
|
{
|
|
for (channel = 0; channel != jack_backend->nb_channels; ++channel)
|
|
{
|
|
if (index == 0)
|
|
{
|
|
if ((len + sampleSize) > rb_data[0].len)
|
|
{
|
|
jack_convert_sample_tx((char*)sampleParts, ((jack_default_audio_sample_t*)buffers[channel])[sample], jack_backend->bit_fmt);
|
|
part = 0;
|
|
while (ptr != (rb_data[0].buf + rb_data[0].len))
|
|
{
|
|
*(ptr) = sampleParts[part];
|
|
ptr++;
|
|
part++;
|
|
}
|
|
ptr = rb_data[1].buf;
|
|
for (; part<VBanBitResolutionSize[jack_backend->bit_fmt]; part++, ptr++)
|
|
{
|
|
*ptr = sampleParts[part];
|
|
}
|
|
|
|
len += sampleSize;
|
|
index = 1;
|
|
continue;
|
|
}
|
|
if (len == rb_data[0].len)
|
|
{
|
|
ptr = rb_data[1].buf;
|
|
index = 1;
|
|
}
|
|
}
|
|
|
|
jack_convert_sample_tx((char*)sampleParts, ((jack_default_audio_sample_t*)buffers[channel])[sample], jack_backend->bit_fmt);
|
|
ptr += sampleSize;
|
|
len += sampleSize;
|
|
}
|
|
}
|
|
|
|
jack_ringbuffer_read_advance(jack_backend->ring_buffer, len);//*/
|
|
|
|
// Realisation of TX callback with the official example
|
|
|
|
for (sample = 0; sample < nframes; sample++)
|
|
{
|
|
if (!(jack_ringbuffer_write_space(jack_backend->ring_buffer)<(sampleSize*jack_backend->nb_channels)))
|
|
{
|
|
for (channel = 0; channel < jack_backend->nb_channels; channel++)
|
|
{
|
|
jack_convert_sample_tx(sampleParts, (((jack_default_audio_sample_t*)buffers[channel])[sample]), jack_backend->bit_fmt);
|
|
if (jack_ringbuffer_write(jack_backend->ring_buffer, sampleParts, sampleSize) < sampleSize) overruns++;
|
|
}
|
|
}
|
|
}
|
|
if (pthread_mutex_trylock(&tx_read_thread_lock)==0)
|
|
{
|
|
pthread_cond_signal(&data_ready);
|
|
pthread_mutex_unlock(&tx_read_thread_lock);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int jack_process_cb_rx(jack_nframes_t nframes, void* arg)
|
|
{
|
|
struct jack_backend_t* const jack_backend = (struct jack_backend_t*)arg;
|
|
size_t channel;
|
|
size_t sample;
|
|
jack_ringbuffer_data_t rb_data[2];
|
|
static jack_default_audio_sample_t* buffers[VBAN_CHANNELS_MAX_NB];
|
|
char const* ptr;
|
|
size_t len;
|
|
size_t sampleSize;
|
|
size_t index = 0;
|
|
size_t part;
|
|
char sampleParts[8];
|
|
|
|
if (arg == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: handle pointer is null", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
logger_log(LOG_DEBUG, "%s", __func__);
|
|
|
|
sampleSize = VBanBitResolutionSize[jack_backend->bit_fmt];
|
|
jack_backend->active = 1;
|
|
|
|
for (channel = 0; channel != jack_backend->nb_channels; ++channel)
|
|
{
|
|
buffers[channel] = (jack_default_audio_sample_t*)jack_port_get_buffer(jack_backend->ports[channel], nframes);
|
|
}
|
|
|
|
jack_ringbuffer_get_read_vector(jack_backend->ring_buffer, rb_data);
|
|
|
|
if ((rb_data[0].len + rb_data[1].len) < (nframes * jack_backend->nb_channels * sampleSize))
|
|
{
|
|
logger_log(LOG_WARNING, "%s: short read", __func__);
|
|
return 0;
|
|
}
|
|
|
|
ptr = rb_data[0].buf;
|
|
len = 0;
|
|
for (sample = 0; sample != nframes; ++sample)
|
|
{
|
|
for (channel = 0; channel != jack_backend->nb_channels; ++channel)
|
|
{
|
|
if (index == 0)
|
|
{
|
|
if ((len + sampleSize) > rb_data[0].len)
|
|
{
|
|
part = 0;
|
|
while (ptr != (rb_data[0].buf + rb_data[0].len))
|
|
{
|
|
sampleParts[part++] = *(ptr++);
|
|
}
|
|
ptr = rb_data[1].buf;
|
|
for (; part < VBanBitResolutionSize[jack_backend->bit_fmt]; ++part, ++ptr)
|
|
{
|
|
sampleParts[part] = *ptr;
|
|
}
|
|
|
|
buffers[channel][sample] = jack_convert_sample_rx((char const*)sampleParts, jack_backend->bit_fmt);
|
|
len += sampleSize;
|
|
index = 1;
|
|
continue;
|
|
}
|
|
if (len == rb_data[0].len)
|
|
{
|
|
ptr = rb_data[1].buf;
|
|
index = 1;
|
|
}
|
|
}
|
|
|
|
buffers[channel][sample] = jack_convert_sample_rx(ptr, jack_backend->bit_fmt);
|
|
ptr += sampleSize;
|
|
len += sampleSize;
|
|
}
|
|
}
|
|
|
|
jack_ringbuffer_read_advance(jack_backend->ring_buffer, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void jack_shutdown_cb(void* arg)
|
|
{
|
|
audio_backend_handle_t const jack_backend = (audio_backend_handle_t)arg;
|
|
|
|
if (arg == 0)
|
|
{
|
|
logger_log(LOG_ERROR, "%s: handle pointer is null");
|
|
return ;
|
|
}
|
|
|
|
jack_close(jack_backend);
|
|
|
|
/*XXX how to notify upper layer that we are done ?*/
|
|
}
|
|
|
|
jack_default_audio_sample_t jack_convert_sample_rx(char const* ptr, enum VBanBitResolution bit_fmt)
|
|
{
|
|
int value;
|
|
|
|
switch (bit_fmt)
|
|
{
|
|
case VBAN_BITFMT_8_INT:
|
|
return (float)(*((int8_t const*)ptr)) / (float)(1 << 7);
|
|
|
|
case VBAN_BITFMT_16_INT:
|
|
return (float)(*((int16_t const*)ptr)) / (float)(1 << 15);
|
|
|
|
case VBAN_BITFMT_24_INT:
|
|
value = (ptr[2] << 16) | ((unsigned char)ptr[1] << 8) | (unsigned char)ptr[0];
|
|
return (float)value / (float)(1 << 23);
|
|
|
|
case VBAN_BITFMT_32_INT:
|
|
return (float)*((int32_t const*)ptr) / (float)(1 << 31);
|
|
|
|
case VBAN_BITFMT_32_FLOAT:
|
|
return *(float const*)ptr;
|
|
|
|
case VBAN_BITFMT_64_FLOAT:
|
|
default:
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
static void jack_convert_sample_tx(char* ptr, jack_default_audio_sample_t sample, enum VBanBitResolution bit_fmt)
|
|
{
|
|
int32_t tmp = 0;
|
|
uint32_t* tp;
|
|
|
|
switch (bit_fmt)
|
|
{
|
|
case VBAN_BITFMT_8_INT:
|
|
tmp = (int8_t)((float)(1<<7)*sample);//roundf(127.0f*sample);
|
|
ptr[0] = tmp&0xFF;
|
|
break;
|
|
case VBAN_BITFMT_16_INT:
|
|
tmp = (int16_t)((float)(1<<15)*sample);//roundf(127.0f*sample);
|
|
ptr[0] = tmp&0xFF;
|
|
ptr[1] = (tmp>>8)&0xFF;
|
|
break;
|
|
case VBAN_BITFMT_24_INT:
|
|
tmp = (int32_t)((float)(1<<23)*sample);//roundf(127.0f*sample);
|
|
ptr[0] = tmp&0xFF;
|
|
ptr[1] = (tmp>>8)&0xFF;
|
|
ptr[2] = (tmp>>16)&0xFF;
|
|
break;
|
|
case VBAN_BITFMT_32_INT:
|
|
ptr[0] = tmp&0xFF;
|
|
ptr[1] = (tmp>>8)&0xFF;
|
|
ptr[2] = (tmp>>16)&0xFF;
|
|
ptr[3] = (tmp>>24)&0xFF;
|
|
break;
|
|
case VBAN_BITFMT_32_FLOAT:
|
|
tp = (uint32_t*)(&sample);
|
|
tmp = *tp;
|
|
ptr[0] = tmp&0xFF;
|
|
ptr[1] = (tmp>>8)&0xFF;
|
|
ptr[2] = (tmp>>16)&0xFF;
|
|
ptr[3] = (tmp>>24)&0xFF;
|
|
break;
|
|
case VBAN_BITFMT_64_FLOAT:
|
|
default:
|
|
break;
|
|
}
|
|
}
|