From 87fc63d4b73bb977c0ebce1d560bd9b34b5f13d9 Mon Sep 17 00:00:00 2001 From: Alexey Yakovenko Date: Tue, 29 Oct 2013 21:11:37 +0100 Subject: use proper api for freq vis --- deadbeef.h | 17 ++++++------- fft.c | 14 +++++------ plugins.c | 1 - plugins/gtkui/widgets.c | 21 ++++++++++------ streamer.c | 67 +++++++++++++++++++++++-------------------------- streamer.h | 4 +-- 6 files changed, 61 insertions(+), 63 deletions(-) diff --git a/deadbeef.h b/deadbeef.h index 1ea892ab..f5ea79e4 100644 --- a/deadbeef.h +++ b/deadbeef.h @@ -330,7 +330,7 @@ enum ddb_audio_data_type_t { // audio memory constants // since 1.5 -#define DDB_AUDIO_MEMORY_FRAMES 256 +#define DDB_FREQ_BANDS 256 // typecasting macros #define DB_PLUGIN(x) ((DB_plugin_t *)(x)) @@ -820,18 +820,15 @@ typedef struct { int (*pl_meta_exists) (DB_playItem_t *it, const char *key); // ******* new 1.5 APIs ******* - // access real-time audio data (e.g. for visualization) - // returns data size in bytes - // fmt and data will be filled with last bytes that came to the output plugin - // data size must be float[DDB_AUDIO_MEMORY_FRAMES] - // type is one of DDB_AUDIO_WAVEFORM and DDB_AUDIO_FREQ - void (*audio_get_waveform_data) (int type, float *data); - // register/unregister for getting continuous wave data // mainly for visualization // ctx must be unique - void (*register_continuous_wavedata_listener) (void *ctx, void (*callback)(void *ctx, ddb_waveformat_t *fmt, const float *data, int nsamples)); - void (*unregister_continuous_wavedata_listener) (void *ctx); + // type is one of DDB_AUDIO_WAVEFORM and DDB_AUDIO_FREQ + // the waveform can be arbitrary large; + // freq data size is always DDB_FREQ_BANDS + void (*register_continuous_wavedata_listener) (void *ctx, int type, void (*callback)(void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples)); + + void (*unregister_continuous_wavedata_listener) (void *ctx, int type); // this is useful to mute/unmute audio, and query the muted status, from // plugins, without touching the volume control diff --git a/fft.c b/fft.c index 7b6a3a75..59d36e91 100644 --- a/fft.c +++ b/fft.c @@ -27,9 +27,9 @@ #include #include -static float hamming[DDB_AUDIO_MEMORY_FRAMES]; /* hamming window, scaled to sum to 1 */ -static int reversed[DDB_AUDIO_MEMORY_FRAMES]; /* bit-reversal table */ -static float complex roots[DDB_AUDIO_MEMORY_FRAMES / 2]; /* N-th roots of unity */ +static float hamming[DDB_FREQ_BANDS]; /* hamming window, scaled to sum to 1 */ +static int reversed[DDB_FREQ_BANDS]; /* bit-reversal table */ +static float complex roots[DDB_FREQ_BANDS / 2]; /* N-th roots of unity */ static int generated = 0; static float LOGN; /* log N (base 2) */ @@ -59,7 +59,7 @@ static void generate_tables (void) if (generated) return; - const int N = DDB_AUDIO_MEMORY_FRAMES; + const int N = DDB_FREQ_BANDS; LOGN = log2(N); for (int n = 0; n < N; n ++) hamming[n] = 1 - 0.85 * cosf (2 * M_PI * n / N); @@ -71,9 +71,9 @@ static void generate_tables (void) generated = 1; } -static void do_fft (float complex a[DDB_AUDIO_MEMORY_FRAMES]) +static void do_fft (float complex a[DDB_FREQ_BANDS]) { - const int N = DDB_AUDIO_MEMORY_FRAMES; + const int N = DDB_FREQ_BANDS; int half = 1; /* (2^s)/2 */ int inv = N / 2; /* N/(2^s) */ @@ -104,7 +104,7 @@ calc_freq (float *data, float *freq) { // fft code shamelessly stolen from audacious // thanks, John - int N = DDB_AUDIO_MEMORY_FRAMES; + int N = DDB_FREQ_BANDS; float complex a[N]; for (int n = 0; n < N; n ++) { a[reversed[n]] = data[n] * hamming[n]; diff --git a/plugins.c b/plugins.c index 8b6d3ff9..b8f10e02 100644 --- a/plugins.c +++ b/plugins.c @@ -341,7 +341,6 @@ static DB_functions_t deadbeef_api = { .plt_get_meta = (int (*) (ddb_playlist_t *handle, const char *key, char *val, int size))plt_get_meta, .pl_meta_exists = (int (*) (DB_playItem_t *it, const char *key))pl_meta_exists, // ******* new 1.5 APIs ******** - .audio_get_waveform_data = audio_get_waveform_data, .register_continuous_wavedata_listener = register_continuous_wavedata_listener, .unregister_continuous_wavedata_listener = unregister_continuous_wavedata_listener, .audio_set_mute = audio_set_mute, diff --git a/plugins/gtkui/widgets.c b/plugins/gtkui/widgets.c index 5132689c..e487bf29 100644 --- a/plugins/gtkui/widgets.c +++ b/plugins/gtkui/widgets.c @@ -146,6 +146,7 @@ typedef struct { #if USE_OPENGL GdkGLContext *glcontext; #endif + float data[DDB_FREQ_BANDS]; } w_spectrum_t; typedef struct { @@ -2236,7 +2237,7 @@ w_coverart_create (void) { void w_scope_destroy (ddb_gtkui_widget_t *w) { w_scope_t *s = (w_scope_t *)w; - deadbeef->unregister_continuous_wavedata_listener (w); + deadbeef->unregister_continuous_wavedata_listener (w, DDB_AUDIO_WAVEFORM); if (s->drawtimer) { g_source_remove (s->drawtimer); s->drawtimer = 0; @@ -2261,7 +2262,7 @@ w_scope_draw_cb (void *data) { } static void -scope_wavedata_listener (void *ctx, ddb_waveformat_t *fmt, const float *data, int in_samples) { +scope_wavedata_listener (void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int in_samples) { w_scope_t *w = ctx; if (w->samples) { // append @@ -2445,7 +2446,7 @@ w_scope_create (void) { #endif g_signal_connect_after (G_OBJECT (w->drawarea), "realize", G_CALLBACK (scope_realize), w); w_override_signals (w->base.widget, w); - deadbeef->register_continuous_wavedata_listener (w, scope_wavedata_listener); + deadbeef->register_continuous_wavedata_listener (w, DDB_AUDIO_WAVEFORM, scope_wavedata_listener); return (ddb_gtkui_widget_t *)w; } @@ -2462,6 +2463,7 @@ w_spectrum_destroy (ddb_gtkui_widget_t *w) { gdk_gl_context_destroy (s->glcontext); s->glcontext = NULL; } + deadbeef->unregister_continuous_wavedata_listener (w, DDB_AUDIO_FREQ); #endif } @@ -2474,7 +2476,7 @@ w_spectrum_draw_cb (void *data) { // spectrum analyzer based on cairo-spectrum from audacious // Copyright (c) 2011 William Pitcock -#define MAX_BANDS DDB_AUDIO_MEMORY_FRAMES +#define MAX_BANDS DDB_FREQ_BANDS #define VIS_DELAY 1 #define VIS_DELAY_PEAK 10 #define VIS_FALLOFF 1 @@ -2494,12 +2496,16 @@ static void calculate_bands(int bands) xscale[i] = powf((float)(MAX_BANDS+1), ((float) i / (float) bands)) - 1; } +static void +spectrum_audio_listener (void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples) { + w_spectrum_t *w = ctx; + memcpy (w->data, data, DDB_FREQ_BANDS * sizeof (float)); +} + static gboolean spectrum_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { w_spectrum_t *w = user_data; - float data[DDB_AUDIO_MEMORY_FRAMES]; - float *freq = data; - deadbeef->audio_get_waveform_data (DDB_AUDIO_FREQ, data); + float *freq = w->data; GtkAllocation a; gtk_widget_get_allocation (widget, &a); @@ -2675,6 +2681,7 @@ w_spectrum_create (void) { #endif g_signal_connect_after (G_OBJECT (w->drawarea), "realize", G_CALLBACK (spectrum_realize), w); w_override_signals (w->base.widget, w); + deadbeef->register_continuous_wavedata_listener (w, DDB_AUDIO_FREQ, spectrum_audio_listener); return (ddb_gtkui_widget_t *)w; } diff --git a/streamer.c b/streamer.c index 674f3517..694b587f 100644 --- a/streamer.c +++ b/streamer.c @@ -105,7 +105,6 @@ static char streambuffer[STREAM_BUFFER_SIZE]; static int bytes_until_next_song = 0; static uintptr_t mutex; static uintptr_t decodemutex; -static uintptr_t audio_mem_mutex; static int nextsong = -1; static int nextsong_pstate = -1; static int badsong = -1; @@ -136,8 +135,9 @@ static int streamer_buffering; static DB_FILE *streamer_file; // for vis plugins -static float freq_data[DDB_AUDIO_MEMORY_FRAMES]; -static float audio_data[DDB_AUDIO_MEMORY_FRAMES]; +static float freq_data[DDB_FREQ_BANDS]; +static float audio_data[DDB_FREQ_BANDS]; +static int audio_data_fill = 0; // message queue static struct handler_s *handler; @@ -145,7 +145,8 @@ static struct handler_s *handler; // visualization stuff typedef struct wavedata_listener_s { void *ctx; - void (*callback)(void *ctx, ddb_waveformat_t *fmt, const float *data, int nsamples); + int type; + void (*callback)(void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples); struct wavedata_listener_s *next; } wavedata_listener_t; @@ -1889,7 +1890,6 @@ streamer_init (void) { #endif mutex = mutex_create (); decodemutex = mutex_create (); - audio_mem_mutex = mutex_create (); ringbuf_init (&streamer_ringbuf, streambuffer, STREAM_BUFFER_SIZE); @@ -1939,8 +1939,6 @@ streamer_free (void) { decodemutex = 0; mutex_free (mutex); mutex = 0; - mutex_free (audio_mem_mutex); - audio_mem_mutex = 0; streamer_dsp_chain_save(); @@ -2280,7 +2278,6 @@ streamer_read (char *bytes, int size) { #endif if (wavedata_listeners) { - mutex_lock (audio_mem_mutex); int in_frame_size = (output->fmt.bps >> 3) * output->fmt.channels; int in_frames = sz / in_frame_size; ddb_waveformat_t out_fmt = { @@ -2294,20 +2291,29 @@ streamer_read (char *bytes, int size) { float temp_audio_data[in_frames * out_fmt.channels]; pcm_convert (&output->fmt, bytes, &out_fmt, (char *)temp_audio_data, sz); - if (in_frames < DDB_AUDIO_MEMORY_FRAMES) { - memmove (audio_data, audio_data + in_frames, (DDB_AUDIO_MEMORY_FRAMES-in_frames)*sizeof (float)); - memcpy (audio_data + DDB_AUDIO_MEMORY_FRAMES - in_frames, temp_audio_data, sz); - } - else { - memcpy (audio_data, temp_audio_data, DDB_AUDIO_MEMORY_FRAMES * in_frame_size); - } - for (wavedata_listener_t *l = wavedata_listeners; l; l = l->next) { - l->callback (l->ctx, &out_fmt, temp_audio_data, in_frames); - } - - calc_freq (audio_data, freq_data); - mutex_unlock (audio_mem_mutex); + if (l->type == DDB_AUDIO_WAVEFORM) { + l->callback (l->ctx, l->type, &out_fmt, temp_audio_data, in_frames); + } + } + + int remaining = in_frames; + do { + int sz = DDB_FREQ_BANDS-audio_data_fill; + sz = min (sz, remaining); + memcpy (&audio_data[audio_data_fill], &temp_audio_data[in_frames-remaining], sz * sizeof (float)); + audio_data_fill += sz; + remaining -= sz; + if (audio_data_fill == DDB_FREQ_BANDS) { + calc_freq (audio_data, freq_data); + for (wavedata_listener_t *l = wavedata_listeners; l; l = l->next) { + if (l->type == DDB_AUDIO_FREQ) { + l->callback (l->ctx, l->type, &out_fmt, freq_data, DDB_FREQ_BANDS); + } + } + audio_data_fill = 0; + } + } while (remaining > 0); } if (!output->has_volume) { @@ -2581,31 +2587,20 @@ streamer_notify_order_changed (int prev_order, int new_order) { } void -audio_get_waveform_data (int type, float *data) { - DB_output_t *output = plug_get_output (); - if (!audio_mem_mutex || !output || output->state () == OUTPUT_STATE_STOPPED) { - memset (data, 0, sizeof (audio_data)); - return; - } - mutex_lock (audio_mem_mutex); - memcpy (data, type == DDB_AUDIO_WAVEFORM ? audio_data : freq_data, sizeof (audio_data)); - mutex_unlock (audio_mem_mutex); -} - -void -register_continuous_wavedata_listener (void *ctx, void (*callback)(void *ctx, ddb_waveformat_t *fmt, const float *data, int nsamples)) { +register_continuous_wavedata_listener (void *ctx, int type, void (*callback)(void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples)) { wavedata_listener_t *l = malloc (sizeof (wavedata_listener_t)); memset (l, 0, sizeof (wavedata_listener_t)); l->ctx = ctx; + l->type = type; l->callback = callback; l->next = wavedata_listeners; wavedata_listeners = l; } void -unregister_continuous_wavedata_listener (void *ctx) { +unregister_continuous_wavedata_listener (void *ctx, int type) { wavedata_listener_t *l, *prev = NULL; - for (l = wavedata_listeners; l && l->ctx != ctx; prev = l, l = l->next); + for (l = wavedata_listeners; l && l->ctx != ctx && l->type != type; prev = l, l = l->next); if (l) { if (prev) { prev->next = l->next; diff --git a/streamer.h b/streamer.h index 96acab9a..fd45662b 100644 --- a/streamer.h +++ b/streamer.h @@ -149,9 +149,9 @@ struct handler_s * streamer_get_handler (void); void -register_continuous_wavedata_listener (void *ctx, void (*callback)(void *ctx, ddb_waveformat_t *fmt, const float *data, int nsamples)); +register_continuous_wavedata_listener (void *ctx, int type, void (*callback)(void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples)); void -unregister_continuous_wavedata_listener (void *ctx); +unregister_continuous_wavedata_listener (void *ctx, int type); #endif // __STREAMER_H -- cgit v1.2.3