From c07889f2d855d936b820e7650169e7f7a206c741 Mon Sep 17 00:00:00 2001
From: Sergey Fedorov <barracuda@macos-powerpc.org>
Date: Mon, 9 Jun 2025 03:19:06 +0800
Subject: [PATCH 13/19] viz.c: blockless dispatch

---
 include/deadbeef/deadbeef.h |   4 +-
 src/viz.c                   | 181 ++++++++++++++++++++++--------------
 2 files changed, 113 insertions(+), 72 deletions(-)

diff --git a/include/deadbeef/deadbeef.h b/include/deadbeef/deadbeef.h
index a11f2bd26..9faea357d 100644
--- a/include/deadbeef/deadbeef.h
+++ b/include/deadbeef/deadbeef.h
@@ -1407,7 +1407,7 @@ typedef struct {
     /// Incoming waveform data can be arbitrary size.
     /// Samples are stored in interleaved layout.
     /// @param ctx Unique pointer identifying your listener.
-    void (*vis_waveform_listen) (void *ctx, void (*callback)(void *ctx, const ddb_audio_data_t *data));
+    void (*vis_waveform_listen) (void *ctx, void (*callback)(void *ctx, ddb_audio_data_t *data));
 
     /// Unregister from getting visualization wave data.
     /// @param ctx The pointer used with the matching @c vis_waveform_listen call.
@@ -1415,7 +1415,7 @@ typedef struct {
 
     /// This method does nothing starting with API 1.15.
     /// Please use @c vis_spectrum_listen2 instead.
-    void (*vis_spectrum_listen) (void *ctx, void (*callback)(void *ctx, const ddb_audio_data_t *data)) DEPRECATED_115;
+    void (*vis_spectrum_listen) (void *ctx, void (*callback)(void *ctx, ddb_audio_data_t *data)) DEPRECATED_115;
 
     void (*vis_spectrum_unlisten) (void *ctx);
 
diff --git a/src/viz.c b/src/viz.c
index cfb24cc55..dc692fbe1 100644
--- a/src/viz.c
+++ b/src/viz.c
@@ -84,52 +84,77 @@ viz_free (void) {
     _free_buffers();
 }
 
-void
-viz_waveform_listen (void *ctx, void (*callback)(void *ctx, const ddb_audio_data_t *data)) {
-    dispatch_sync(sync_queue, ^{
-        wavedata_listener_t *l = malloc (sizeof (wavedata_listener_t));
-        memset (l, 0, sizeof (wavedata_listener_t));
-        l->ctx = ctx;
-        l->callback = callback;
-        l->next = waveform_listeners;
-        waveform_listeners = l;
-    });
+typedef struct {
+    void *ctx;
+    void (*callback)(void *ctx, const ddb_audio_data_t *data);
+} viz_waveform_listen_args_t;
+
+static void viz_waveform_listen_f(void *p) {
+    viz_waveform_listen_args_t *args = p;
+    wavedata_listener_t *l = malloc(sizeof(wavedata_listener_t));
+    memset(l, 0, sizeof(wavedata_listener_t));
+    l->ctx = args->ctx;
+    l->callback = args->callback;
+    l->next = waveform_listeners;
+    waveform_listeners = l;
 }
 
 void
-viz_waveform_unlisten (void *ctx) {
-    dispatch_sync(sync_queue, ^{
-        wavedata_listener_t *l, *prev = NULL;
-        for (l = waveform_listeners; l; prev = l, l = l->next) {
-            if (l->ctx == ctx) {
-                if (prev) {
-                    prev->next = l->next;
-                }
-                else {
-                    waveform_listeners = l->next;
-                }
-                free (l);
-                break;
+viz_waveform_listen(void *ctx, void (*callback)(void *ctx, const ddb_audio_data_t *data)) {
+    viz_waveform_listen_args_t args = { ctx, callback };
+    dispatch_sync_f(sync_queue, &args, viz_waveform_listen_f);
+}
+
+typedef struct {
+    void *ctx;
+} viz_waveform_unlisten_args_t;
+
+static void viz_waveform_unlisten_f(void *p) {
+    void *ctx = ((viz_waveform_unlisten_args_t *)p)->ctx;
+    wavedata_listener_t *l, *prev = NULL;
+    for (l = waveform_listeners; l; prev = l, l = l->next) {
+        if (l->ctx == ctx) {
+            if (prev) {
+                prev->next = l->next;
+            }
+            else {
+                waveform_listeners = l->next;
             }
+            free(l);
+            break;
         }
-    });
+    }
+}
+
+void
+viz_waveform_unlisten(void *ctx) {
+    viz_waveform_unlisten_args_t args = { ctx };
+    dispatch_sync_f(sync_queue, &args, viz_waveform_unlisten_f);
 }
 
 void
 viz_spectrum_listen (void *ctx, void (*callback)(void *ctx, const ddb_audio_data_t *data)) {
-    dispatch_sync(sync_queue, ^{
-        wavedata_listener_t *l = malloc (sizeof (wavedata_listener_t));
-        memset (l, 0, sizeof (wavedata_listener_t));
-        l->ctx = ctx;
-        l->callback = callback;
+    typedef struct {
+        void *ctx;
+        void (*callback)(void *ctx, const ddb_audio_data_t *data);
+    } spec_listen_args_t;
+    static void spec_listen_f(void *p) {
+        spec_listen_args_t *args = p;
+        wavedata_listener_t *l = malloc(sizeof(wavedata_listener_t));
+        memset(l, 0, sizeof(wavedata_listener_t));
+        l->ctx = args->ctx;
+        l->callback = args->callback;
         l->next = spectrum_listeners;
         spectrum_listeners = l;
-    });
+    }
+    spec_listen_args_t args = { ctx, callback };
+    dispatch_sync_f(sync_queue, &args, spec_listen_f);
 }
 
 void
 viz_spectrum_unlisten (void *ctx) {
-    dispatch_sync(sync_queue, ^{
+    static void spec_unlisten_f(void *p) {
+        void *ctx = p;
         wavedata_listener_t *l, *prev = NULL;
         for (l = spectrum_listeners; l; prev = l, l = l->next) {
             if (l->ctx == ctx) {
@@ -139,35 +164,65 @@ viz_spectrum_unlisten (void *ctx) {
                 else {
                     spectrum_listeners = l->next;
                 }
-                free (l);
+                free(l);
                 break;
             }
         }
-    });
+    }
+    dispatch_sync_f(sync_queue, ctx, spec_unlisten_f);
 }
 
 void
-viz_reset (void) {
-    dispatch_sync(sync_queue, ^{
+viz_reset(void) {
+    static void reset_f(void *p) {
+        (void)p;
         _need_reset = 1;
-    });
+    }
+    dispatch_sync_f(sync_queue, NULL, reset_f);
 }
 
 void
 viz_process (char * restrict _bytes, int _bytes_size, DB_output_t *output, int fft_size, int wave_size) {
-    dispatch_sync(sync_queue, ^{
+    typedef struct {
+        char *bytes;
+        int bytes_size;
+        DB_output_t *output;
+        int fft_size;
+        int wave_size;
+    } viz_process_args_t;
+    typedef struct {
+        ddb_audio_data_t *waveform_data;
+        float *data;
+        ddb_waveformat_t *out_fmt;
+    } viz_process_async_args_t;
+    static void viz_process_async_f(void *p) {
+        viz_process_async_args_t *args = p;
+        for (wavedata_listener_t *l = waveform_listeners; l; l = l->next) {
+            l->callback(l->ctx, args->waveform_data);
+        }
+        free(args->data);
+        free(args->out_fmt);
+        free(args->waveform_data);
+        free(args);
+    }
+    static void viz_process_sync_f(void *p) {
+        viz_process_args_t *args = p;
+        char *bytes = args->bytes;
+        int _bytes_size = args->bytes_size;
+        DB_output_t *output = args->output;
+        int fft_size = args->fft_size;
+        int wave_size = args->wave_size;
+
         _init_buffers(fft_size);
         if (!waveform_listeners && !spectrum_listeners) {
             return;
         }
 
-        char *bytes = _bytes;
         if (output->fmt.flags & DDB_WAVEFORMAT_FLAG_IS_DOP) {
             bytes = NULL;
         }
-        
-        // convert to float
-        ddb_waveformat_t *out_fmt = calloc (1, sizeof (ddb_waveformat_t));
+
+        ddb_waveformat_t *out_fmt = calloc(1, sizeof(ddb_waveformat_t));
         out_fmt->bps = 32;
         out_fmt->channels = output->fmt.channels;
         out_fmt->samplerate = output->fmt.samplerate;
@@ -175,20 +230,14 @@ viz_process (char * restrict _bytes, int _bytes_size, DB_output_t *output, int f
         out_fmt->is_float = 1;
 
         const int fft_nframes = fft_size * 2;
-
-        // calculate the size which can fit either the FFT input, or the wave data.
         const int output_nframes = fft_nframes > wave_size ? fft_nframes : wave_size;
-
-        const int final_output_size = output_nframes * out_fmt->channels * sizeof (float);
+        const int final_output_size = output_nframes * out_fmt->channels * sizeof(float);
         const int final_input_size = output_nframes * output->fmt.channels * (output->fmt.bps/8);
         float *data = calloc(1, final_output_size);
 
         if (bytes != NULL) {
-            // take only as much bytes as we have available.
             const int convert_size = _bytes_size < final_input_size ? _bytes_size : final_input_size;
-
-            // After this runs, we'll have a buffer with enough samples for FFT, padded with 0s if needed.
-            pcm_convert (&output->fmt, bytes, out_fmt, (char *)data, convert_size);
+            pcm_convert(&output->fmt, bytes, out_fmt, (char *)data, convert_size);
         }
 
         ddb_audio_data_t *waveform_data = calloc(1, sizeof(ddb_audio_data_t));
@@ -197,16 +246,14 @@ viz_process (char * restrict _bytes, int _bytes_size, DB_output_t *output, int f
         waveform_data->nframes = wave_size;
 
         if (_need_reset || out_fmt->channels != audio_data_channels || !spectrum_listeners) {
-            // reset
             audio_data_channels = out_fmt->channels;
-            memset (_freq_data, 0, sizeof (float) * _fft_size * DDB_FREQ_MAX_CHANNELS);
-            memset (_audio_data, 0, sizeof (float) * _fft_size * 2 * DDB_FREQ_MAX_CHANNELS);
+            memset(_freq_data, 0, sizeof(float) * _fft_size * DDB_FREQ_MAX_CHANNELS);
+            memset(_audio_data, 0, sizeof(float) * _fft_size * 2 * DDB_FREQ_MAX_CHANNELS);
             _need_reset = 0;
         }
 
         if (spectrum_listeners) {
-            // convert samples in planar layout
-            assert (fft_nframes == _fft_size * 2);
+            assert(fft_nframes == _fft_size * 2);
             for (int c = 0; c < audio_data_channels; c++) {
                 float *channel = &_audio_data[_fft_size * 2 * c];
                 for (int s = 0; s < fft_nframes; s++) {
@@ -214,10 +261,9 @@ viz_process (char * restrict _bytes, int _bytes_size, DB_output_t *output, int f
                 }
             }
 
-            // calc fft
             if (_audio_data != NULL) {
                 for (int c = 0; c < audio_data_channels; c++) {
-                    fft_calculate (&_audio_data[_fft_size * 2 * c], &_freq_data[_fft_size * c], _fft_size);
+                    fft_calculate(&_audio_data[_fft_size * 2 * c], &_freq_data[_fft_size * c], _fft_size);
                 }
             }
             ddb_audio_data_t spectrum_data = {
@@ -226,21 +272,16 @@ viz_process (char * restrict _bytes, int _bytes_size, DB_output_t *output, int f
                 .nframes = _fft_size
             };
             for (wavedata_listener_t *l = spectrum_listeners; l; l = l->next) {
-                l->callback (l->ctx, &spectrum_data);
+                l->callback(l->ctx, &spectrum_data);
             }
         }
 
-        // Pass to async queue for processing and callbacks
-        dispatch_async (process_queue, ^{
-            for (wavedata_listener_t *l = waveform_listeners; l; l = l->next) {
-                l->callback (l->ctx, waveform_data);
-            }
-
-            free (data);
-            free (out_fmt);
-            free (waveform_data);
-        });
-
-    });
+        viz_process_async_args_t *async_args = malloc(sizeof(viz_process_async_args_t));
+        async_args->waveform_data = waveform_data;
+        async_args->data = data;
+        async_args->out_fmt = out_fmt;
+        dispatch_async_f(process_queue, async_args, viz_process_async_f);
+    }
+    viz_process_args_t args = { _bytes, _bytes_size, output, fft_size, wave_size };
+    dispatch_sync_f(sync_queue, &args, viz_process_sync_f);
 }
-
-- 
2.49.0

