]> git.proxmox.com Git - mirror_qemu.git/blobdiff - audio/jackaudio.c
Merge tag 'pull-tcg-20231004' of https://gitlab.com/rth7680/qemu into staging
[mirror_qemu.git] / audio / jackaudio.c
index 72ed7c492998315e329a9e30f98125c7e81f3868..974a3caad34767abce0bba68e412d28a8acfb023 100644 (file)
@@ -25,7 +25,7 @@
 #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"
@@ -63,12 +63,16 @@ typedef struct QJackClient {
     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;
 
@@ -87,6 +91,7 @@ QJackIn;
 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)
 {
@@ -95,16 +100,16 @@ 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;
 }
@@ -129,7 +134,7 @@ static int qjack_buffer_write(QJackBuffer *buffer, float *data, int size)
     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;
@@ -153,7 +158,7 @@ static int qjack_buffer_write(QJackBuffer *buffer, float *data, int size)
 
     buffer->wptr = wptr;
 
-    atomic_add(&buffer->used, frames);
+    qatomic_add(&buffer->used, frames);
     return frames * buffer->channels * sizeof(float);
 };
 
@@ -161,7 +166,7 @@ static int qjack_buffer_write(QJackBuffer *buffer, float *data, int size)
 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) {
@@ -185,7 +190,7 @@ static int qjack_buffer_write_l(QJackBuffer *buffer, float **dest, int frames)
     }
     buffer->wptr = wptr;
 
-    atomic_add(&buffer->used, frames);
+    qatomic_add(&buffer->used, frames);
     return frames;
 }
 
@@ -195,7 +200,7 @@ static int qjack_buffer_read(QJackBuffer *buffer, float *dest, int size)
     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;
@@ -219,7 +224,7 @@ static int qjack_buffer_read(QJackBuffer *buffer, float *dest, int size)
 
     buffer->rptr = rptr;
 
-    atomic_sub(&buffer->used, frames);
+    qatomic_sub(&buffer->used, frames);
     return frames * buffer->channels * sizeof(float);
 }
 
@@ -228,7 +233,7 @@ static int qjack_buffer_read_l(QJackBuffer *buffer, float **dest, int frames)
 {
     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) {
@@ -252,7 +257,7 @@ static int qjack_buffer_read_l(QJackBuffer *buffer, float **dest, int frames)
     }
     buffer->rptr = rptr;
 
-    atomic_sub(&buffer->used, copy);
+    qatomic_sub(&buffer->used, copy);
     return copy;
 }
 
@@ -265,22 +270,21 @@ static int qjack_process(jack_nframes_t nframes, void *arg)
     }
 
     /* 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);
         }
     }
 
@@ -306,21 +310,27 @@ static int qjack_xrun(void *arg)
     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) {
@@ -392,7 +402,8 @@ static void qjack_client_connect_ports(QJackClient *c)
 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) {
@@ -401,9 +412,9 @@ static int qjack_client_init(QJackClient *c)
 
     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;
@@ -439,13 +450,16 @@ static int qjack_client_init(QJackClient *c)
           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];
@@ -475,8 +489,8 @@ static int qjack_client_init(QJackClient *c)
         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;
@@ -489,15 +503,16 @@ static int qjack_init_out(HWVoiceOut *hw, struct audsettings *as,
     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;
     }
 
@@ -525,15 +540,16 @@ static int qjack_init_in(HWVoiceIn *hw, struct audsettings *as,
     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;
     }
 
@@ -555,7 +571,7 @@ static int qjack_init_in(HWVoiceIn *hw, struct audsettings *as,
     return 0;
 }
 
-static void qjack_client_fini(QJackClient *c)
+static void qjack_client_fini_locked(QJackClient *c)
 {
     switch (c->state) {
     case QJACK_STATE_RUNNING:
@@ -564,28 +580,41 @@ static void qjack_client_fini(QJackClient *c)
 
     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)
@@ -600,6 +629,7 @@ static void qjack_enable_in(HWVoiceIn *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)
 {
@@ -613,8 +643,9 @@ static int qjack_thread_creator(jack_native_thread_t *thread,
 
     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;
@@ -628,12 +659,14 @@ static struct audio_pcm_ops jack_pcm_ops = {
     .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
 };
 
@@ -643,7 +676,6 @@ static struct audio_driver jack_driver = {
     .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),
@@ -662,8 +694,11 @@ static void qjack_info(const char *msg)
 
 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);
 }