#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/atomic.h"
-#include "qemu-common.h"
+#include "qemu/main-loop.h"
#include "audio.h"
#define AUDIO_CAP "jack"
QJackState state;
jack_client_t *client;
jack_nframes_t freq;
+ QEMUBH *shutdown_bh;
struct QJack *j;
int nchannels;
int buffersize;
jack_port_t **port;
QJackBuffer fifo;
+
+ /* Used as workspace by qjack_process() */
+ float **process_buffers;
}
QJackClient;
static int qjack_client_init(QJackClient *c);
static void qjack_client_connect_ports(QJackClient *c);
static void qjack_client_fini(QJackClient *c);
+static QemuMutex qjack_shutdown_lock;
static void qjack_buffer_create(QJackBuffer *buffer, int channels, int frames)
{
buffer->used = 0;
buffer->rptr = 0;
buffer->wptr = 0;
- buffer->data = g_malloc(channels * sizeof(float *));
+ buffer->data = g_new(float *, channels);
for (int i = 0; i < channels; ++i) {
- buffer->data[i] = g_malloc(frames * sizeof(float));
+ buffer->data[i] = g_new(float, frames);
}
}
static void qjack_buffer_clear(QJackBuffer *buffer)
{
assert(buffer->data);
- atomic_store_release(&buffer->used, 0);
+ qatomic_store_release(&buffer->used, 0);
buffer->rptr = 0;
buffer->wptr = 0;
}
assert(buffer->data);
const int samples = size / sizeof(float);
int frames = samples / buffer->channels;
- const int avail = buffer->frames - atomic_load_acquire(&buffer->used);
+ const int avail = buffer->frames - qatomic_load_acquire(&buffer->used);
if (frames > avail) {
frames = avail;
buffer->wptr = wptr;
- atomic_add(&buffer->used, frames);
+ qatomic_add(&buffer->used, frames);
return frames * buffer->channels * sizeof(float);
};
static int qjack_buffer_write_l(QJackBuffer *buffer, float **dest, int frames)
{
assert(buffer->data);
- const int avail = buffer->frames - atomic_load_acquire(&buffer->used);
+ const int avail = buffer->frames - qatomic_load_acquire(&buffer->used);
int wptr = buffer->wptr;
if (frames > avail) {
}
buffer->wptr = wptr;
- atomic_add(&buffer->used, frames);
+ qatomic_add(&buffer->used, frames);
return frames;
}
assert(buffer->data);
const int samples = size / sizeof(float);
int frames = samples / buffer->channels;
- const int avail = atomic_load_acquire(&buffer->used);
+ const int avail = qatomic_load_acquire(&buffer->used);
if (frames > avail) {
frames = avail;
buffer->rptr = rptr;
- atomic_sub(&buffer->used, frames);
+ qatomic_sub(&buffer->used, frames);
return frames * buffer->channels * sizeof(float);
}
{
assert(buffer->data);
int copy = frames;
- const int used = atomic_load_acquire(&buffer->used);
+ const int used = qatomic_load_acquire(&buffer->used);
int rptr = buffer->rptr;
if (copy > used) {
}
buffer->rptr = rptr;
- atomic_sub(&buffer->used, copy);
+ qatomic_sub(&buffer->used, copy);
return copy;
}
}
/* get the buffers for the ports */
- float *buffers[c->nchannels];
for (int i = 0; i < c->nchannels; ++i) {
- buffers[i] = jack_port_get_buffer(c->port[i], nframes);
+ c->process_buffers[i] = jack_port_get_buffer(c->port[i], nframes);
}
if (c->out) {
if (likely(c->enabled)) {
- qjack_buffer_read_l(&c->fifo, buffers, nframes);
+ qjack_buffer_read_l(&c->fifo, c->process_buffers, nframes);
} else {
- for(int i = 0; i < c->nchannels; ++i) {
- memset(buffers[i], 0, nframes * sizeof(float));
+ for (int i = 0; i < c->nchannels; ++i) {
+ memset(c->process_buffers[i], 0, nframes * sizeof(float));
}
}
} else {
if (likely(c->enabled)) {
- qjack_buffer_write_l(&c->fifo, buffers, nframes);
+ qjack_buffer_write_l(&c->fifo, c->process_buffers, nframes);
}
}
return 0;
}
+static void qjack_shutdown_bh(void *opaque)
+{
+ QJackClient *c = (QJackClient *)opaque;
+ qjack_client_fini(c);
+}
+
static void qjack_shutdown(void *arg)
{
QJackClient *c = (QJackClient *)arg;
c->state = QJACK_STATE_SHUTDOWN;
+ qemu_bh_schedule(c->shutdown_bh);
}
static void qjack_client_recover(QJackClient *c)
{
- if (c->state == QJACK_STATE_SHUTDOWN) {
- qjack_client_fini(c);
+ if (c->state != QJACK_STATE_DISCONNECTED) {
+ return;
}
/* packets is used simply to throttle this */
- if (c->state == QJACK_STATE_DISCONNECTED &&
- c->packets % 100 == 0) {
+ if (c->packets % 100 == 0) {
/* if enabled then attempt to recover */
if (c->enabled) {
static int qjack_client_init(QJackClient *c)
{
jack_status_t status;
- char client_name[jack_client_name_size()];
+ int client_name_len = jack_client_name_size(); /* includes NUL */
+ g_autofree char *client_name = g_new(char, client_name_len);
jack_options_t options = JackNullOption;
if (c->state == QJACK_STATE_RUNNING) {
c->connect_ports = true;
- snprintf(client_name, sizeof(client_name), "%s-%s",
+ snprintf(client_name, client_name_len, "%s-%s",
c->out ? "out" : "in",
- c->opt->client_name ? c->opt->client_name : qemu_get_vm_name());
+ c->opt->client_name ? c->opt->client_name : audio_application_name());
if (c->opt->exact_name) {
options |= JackUseExactName;
jack_get_client_name(c->client));
}
+ /* Allocate working buffer for process callback */
+ c->process_buffers = g_new(float *, c->nchannels);
+
jack_set_process_callback(c->client, qjack_process , c);
jack_set_port_registration_callback(c->client, qjack_port_registration, c);
jack_set_xrun_callback(c->client, qjack_xrun, c);
jack_on_shutdown(c->client, qjack_shutdown, c);
/* allocate and register the ports */
- c->port = g_malloc(sizeof(jack_port_t *) * c->nchannels);
+ c->port = g_new(jack_port_t *, c->nchannels);
for (int i = 0; i < c->nchannels; ++i) {
char port_name[16];
c->buffersize = 512;
}
- /* create a 2 period buffer */
- qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 2);
+ /* create a 3 period buffer */
+ qjack_buffer_create(&c->fifo, c->nchannels, c->buffersize * 3);
qjack_client_connect_ports(c);
c->state = QJACK_STATE_RUNNING;
QJackOut *jo = (QJackOut *)hw;
Audiodev *dev = (Audiodev *)drv_opaque;
- qjack_client_fini(&jo->c);
-
jo->c.out = true;
jo->c.enabled = false;
jo->c.nchannels = as->nchannels;
jo->c.opt = dev->u.jack.out;
+ jo->c.shutdown_bh = qemu_bh_new(qjack_shutdown_bh, &jo->c);
+
int ret = qjack_client_init(&jo->c);
if (ret != 0) {
+ qemu_bh_delete(jo->c.shutdown_bh);
return ret;
}
QJackIn *ji = (QJackIn *)hw;
Audiodev *dev = (Audiodev *)drv_opaque;
- qjack_client_fini(&ji->c);
-
ji->c.out = false;
ji->c.enabled = false;
ji->c.nchannels = as->nchannels;
ji->c.opt = dev->u.jack.in;
+ ji->c.shutdown_bh = qemu_bh_new(qjack_shutdown_bh, &ji->c);
+
int ret = qjack_client_init(&ji->c);
if (ret != 0) {
+ qemu_bh_delete(ji->c.shutdown_bh);
return ret;
}
return 0;
}
-static void qjack_client_fini(QJackClient *c)
+static void qjack_client_fini_locked(QJackClient *c)
{
switch (c->state) {
case QJACK_STATE_RUNNING:
case QJACK_STATE_SHUTDOWN:
jack_client_close(c->client);
+ c->client = NULL;
+
+ qjack_buffer_free(&c->fifo);
+ g_free(c->port);
+ g_free(c->process_buffers);
+
+ c->state = QJACK_STATE_DISCONNECTED;
/* fallthrough */
case QJACK_STATE_DISCONNECTED:
break;
}
+}
- qjack_buffer_free(&c->fifo);
- g_free(c->port);
-
- c->state = QJACK_STATE_DISCONNECTED;
+static void qjack_client_fini(QJackClient *c)
+{
+ qemu_mutex_lock(&qjack_shutdown_lock);
+ qjack_client_fini_locked(c);
+ qemu_mutex_unlock(&qjack_shutdown_lock);
}
static void qjack_fini_out(HWVoiceOut *hw)
{
QJackOut *jo = (QJackOut *)hw;
qjack_client_fini(&jo->c);
+
+ qemu_bh_delete(jo->c.shutdown_bh);
}
static void qjack_fini_in(HWVoiceIn *hw)
{
QJackIn *ji = (QJackIn *)hw;
qjack_client_fini(&ji->c);
+
+ qemu_bh_delete(ji->c.shutdown_bh);
}
static void qjack_enable_out(HWVoiceOut *hw, bool enable)
ji->c.enabled = enable;
}
+#if !defined(WIN32) && defined(CONFIG_PTHREAD_SETNAME_NP_W_TID)
static int qjack_thread_creator(jack_native_thread_t *thread,
const pthread_attr_t *attr, void *(*function)(void *), void *arg)
{
return ret;
}
+#endif
-static void *qjack_init(Audiodev *dev)
+static void *qjack_init(Audiodev *dev, Error **errp)
{
assert(dev->driver == AUDIODEV_DRIVER_JACK);
return dev;
.init_out = qjack_init_out,
.fini_out = qjack_fini_out,
.write = qjack_write,
+ .buffer_get_free = audio_generic_buffer_get_free,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = qjack_enable_out,
.init_in = qjack_init_in,
.fini_in = qjack_fini_in,
.read = qjack_read,
+ .run_buffer_in = audio_generic_run_buffer_in,
.enable_in = qjack_enable_in
};
.init = qjack_init,
.fini = qjack_fini,
.pcm_ops = &jack_pcm_ops,
- .can_be_default = 1,
.max_voices_out = INT_MAX,
.max_voices_in = INT_MAX,
.voice_size_out = sizeof(QJackOut),
static void register_audio_jack(void)
{
+ qemu_mutex_init(&qjack_shutdown_lock);
audio_driver_register(&jack_driver);
+#if !defined(WIN32) && defined(CONFIG_PTHREAD_SETNAME_NP_W_TID)
jack_set_thread_creator(qjack_thread_creator);
+#endif
jack_set_error_function(qjack_error);
jack_set_info_function(qjack_info);
}