diff options
author | Alexey Yakovenko <waker@users.sourceforge.net> | 2013-11-01 21:41:14 +0100 |
---|---|---|
committer | Alexey Yakovenko <waker@users.sourceforge.net> | 2013-11-01 21:41:14 +0100 |
commit | 87a915f831fa97cec0e7c061345c6bd1223decd3 (patch) | |
tree | bcc52abe2498a37551f5eca64029ae670a34e1b5 | |
parent | f30a353184a725089ec3f775632f22802e296e89 (diff) |
improved vis API
-rw-r--r-- | deadbeef.h | 37 | ||||
-rw-r--r-- | plugins.c | 6 | ||||
-rw-r--r-- | plugins/gtkui/widgets.c | 28 | ||||
-rw-r--r-- | streamer.c | 115 | ||||
-rw-r--r-- | streamer.h | 10 |
5 files changed, 123 insertions, 73 deletions
@@ -322,17 +322,6 @@ enum ddb_sort_order_t { DDB_SORT_RANDOM, // available since API 1.3 }; -// since 1.5 -enum ddb_audio_data_type_t { - DDB_AUDIO_WAVEFORM, - DDB_AUDIO_FREQ, -}; - -// audio memory constants -// since 1.5 -#define DDB_FREQ_BANDS 256 -#define DDB_FREQ_MAX_CHANNELS 9 - // typecasting macros #define DB_PLUGIN(x) ((DB_plugin_t *)(x)) #define DB_CALLBACK(x) ((DB_callback_t)(x)) @@ -358,6 +347,15 @@ typedef struct { int is_bigendian; } ddb_waveformat_t; +// since 1.5 +#define DDB_FREQ_BANDS 256 +#define DDB_FREQ_MAX_CHANNELS 9 +typedef struct { + const ddb_waveformat_t *fmt; + const float *data; + int nframes; +} ddb_audio_data_t; + // forward decl for plugin struct struct DB_plugin_s; @@ -824,12 +822,19 @@ typedef struct { // register/unregister for getting continuous wave data // mainly for visualization // ctx must be unique - // 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)); + // the waveform data can be arbitrary size + // the samples are interleaved + void (*vis_waveform_listen) (void *ctx, void (*callback)(void *ctx, ddb_audio_data_t *data)); + void (*vis_waveform_unlisten) (void *ctx); - void (*unregister_continuous_wavedata_listener) (void *ctx, int type); + // register/unregister for getting continuous spectrum (frequency domain) data + // mainly for visualization + // ctx must be unique + // the data always contains DDB_FREQ_BANDS frames + // max number of channels is DDB_FREQ_MAX_CHANNELS + // the samples are non-interleaved + void (*vis_spectrum_listen) (void *ctx, void (*callback)(void *ctx, ddb_audio_data_t *data)); + void (*vis_spectrum_unlisten) (void *ctx); // this is useful to mute/unmute audio, and query the muted status, from // plugins, without touching the volume control @@ -341,8 +341,10 @@ 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 ******** - .register_continuous_wavedata_listener = register_continuous_wavedata_listener, - .unregister_continuous_wavedata_listener = unregister_continuous_wavedata_listener, + .vis_waveform_listen = vis_waveform_listen, + .vis_waveform_unlisten = vis_waveform_unlisten, + .vis_spectrum_listen = vis_spectrum_listen, + .vis_spectrum_unlisten = vis_spectrum_unlisten, .audio_set_mute = audio_set_mute, .audio_is_mute = audio_is_mute, .background_job_increment = background_job_increment, diff --git a/plugins/gtkui/widgets.c b/plugins/gtkui/widgets.c index 45b7554f..4a676c4b 100644 --- a/plugins/gtkui/widgets.c +++ b/plugins/gtkui/widgets.c @@ -157,7 +157,7 @@ typedef struct { #if USE_OPENGL GdkGLContext *glcontext; #endif - float data[DDB_FREQ_BANDS]; + float data[DDB_FREQ_BANDS * DDB_FREQ_MAX_CHANNELS]; float xscale[MAX_BANDS + 1]; int bars[MAX_BANDS + 1]; int delay[MAX_BANDS + 1]; @@ -2253,7 +2253,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, DDB_AUDIO_WAVEFORM); + deadbeef->vis_waveform_unlisten (w); if (s->drawtimer) { g_source_remove (s->drawtimer); s->drawtimer = 0; @@ -2286,7 +2286,7 @@ w_scope_draw_cb (void *data) { } static void -scope_wavedata_listener (void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int in_samples) { +scope_wavedata_listener (void *ctx, ddb_audio_data_t *data) { w_scope_t *w = ctx; if (w->nsamples != w->resized) { deadbeef->mutex_lock (w->mutex); @@ -2310,8 +2310,8 @@ scope_wavedata_listener (void *ctx, int type, ddb_waveformat_t *fmt, const float if (w->samples) { // append - int nsamples = in_samples / fmt->channels; - float ratio = fmt->samplerate / 44100.f; + int nsamples = data->nframes / data->fmt->channels; + float ratio = data->fmt->samplerate / 44100.f; int size = nsamples / ratio; int sz = min (w->nsamples, size); @@ -2320,11 +2320,11 @@ scope_wavedata_listener (void *ctx, int type, ddb_waveformat_t *fmt, const float memmove (w->samples, w->samples + sz, n * sizeof (float)); float pos = 0; for (int i = 0; i < sz && pos < nsamples; i++, pos += ratio) { - w->samples[n + i] = data[ftoi(pos * fmt->channels) * fmt->channels + 0]; - for (int j = 1; j < fmt->channels; j++) { - w->samples[n + i] += data[ftoi(pos * fmt->channels) * fmt->channels + j]; + w->samples[n + i] = data->data[ftoi(pos * data->fmt->channels) * data->fmt->channels + 0]; + for (int j = 1; j < data->fmt->channels; j++) { + w->samples[n + i] += data->data[ftoi(pos * data->fmt->channels) * data->fmt->channels + j]; } - w->samples[n+i] /= fmt->channels; + w->samples[n+i] /= data->fmt->channels; } } } @@ -2518,7 +2518,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, DDB_AUDIO_WAVEFORM, scope_wavedata_listener); + deadbeef->vis_waveform_listen(w, scope_wavedata_listener); return (ddb_gtkui_widget_t *)w; } @@ -2526,6 +2526,7 @@ w_scope_create (void) { void w_spectrum_destroy (ddb_gtkui_widget_t *w) { w_spectrum_t *s = (w_spectrum_t *)w; + deadbeef->vis_spectrum_unlisten (w); if (s->drawtimer) { g_source_remove (s->drawtimer); s->drawtimer = 0; @@ -2535,7 +2536,6 @@ 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 } @@ -2556,9 +2556,9 @@ static void calculate_bands(w_spectrum_t *w, int bands) } static void -spectrum_audio_listener (void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples) { +spectrum_audio_listener (void *ctx, ddb_audio_data_t *data) { w_spectrum_t *w = ctx; - memcpy (w->data, data, DDB_FREQ_BANDS * sizeof (float)); + memcpy (w->data, data->data, DDB_FREQ_BANDS * sizeof (float)); } static gboolean @@ -2739,7 +2739,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); + deadbeef->vis_spectrum_listen (w, spectrum_audio_listener); return (ddb_gtkui_widget_t *)w; } @@ -147,12 +147,12 @@ static struct handler_s *handler; // visualization stuff typedef struct wavedata_listener_s { void *ctx; - int type; - void (*callback)(void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples); + void (*callback)(void *ctx, ddb_audio_data_t *data); struct wavedata_listener_s *next; } wavedata_listener_t; -static wavedata_listener_t *wavedata_listeners; +static wavedata_listener_t *waveform_listeners; +static wavedata_listener_t *spectrum_listeners; #if DETECT_PL_LOCK_RC volatile pthread_t streamer_lock_tid = 0; @@ -2282,7 +2282,7 @@ streamer_read (char *bytes, int size) { printf ("streamer_read took %d ms\n", ms); #endif - if (wavedata_listeners) { + if (waveform_listeners || spectrum_listeners) { int in_frame_size = (output->fmt.bps >> 3) * output->fmt.channels; int in_frames = sz / in_frame_size; ddb_waveformat_t out_fmt = { @@ -2296,44 +2296,51 @@ 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); + ddb_audio_data_t data; + data.fmt = &out_fmt; + data.data = temp_audio_data; + data.nframes = in_frames; mutex_lock (wdl_mutex); - for (wavedata_listener_t *l = wavedata_listeners; l; l = l->next) { - if (l->type == DDB_AUDIO_WAVEFORM) { - l->callback (l->ctx, l->type, &out_fmt, temp_audio_data, in_frames); - } + for (wavedata_listener_t *l = waveform_listeners; l; l = l->next) { + l->callback (l->ctx, &data); } mutex_unlock (wdl_mutex); - if (out_fmt.channels != audio_data_channels) { + if (out_fmt.channels != audio_data_channels || !spectrum_listeners) { audio_data_fill = 0; audio_data_channels = out_fmt.channels; } - int remaining = in_frames; - do { - int sz = DDB_FREQ_BANDS-audio_data_fill; - sz = min (sz, remaining); - for (int c = 0; c < audio_data_channels; c++) { - for (int s = 0; s < sz; s++) { - audio_data[DDB_FREQ_BANDS * c + audio_data_fill + s] = temp_audio_data[(in_frames-remaining + s) * audio_data_channels + c]; - } - } -// 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) { + + if (spectrum_listeners) { + int remaining = in_frames; + do { + int sz = DDB_FREQ_BANDS-audio_data_fill; + sz = min (sz, remaining); for (int c = 0; c < audio_data_channels; c++) { - calc_freq (&audio_data[DDB_FREQ_BANDS * c], &freq_data[DDB_FREQ_BANDS * c]); + for (int s = 0; s < sz; s++) { + audio_data[DDB_FREQ_BANDS * c + audio_data_fill + s] = temp_audio_data[(in_frames-remaining + s) * audio_data_channels + c]; + } } - mutex_lock (wdl_mutex); - 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); + // 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) { + for (int c = 0; c < audio_data_channels; c++) { + calc_freq (&audio_data[DDB_FREQ_BANDS * c], &freq_data[DDB_FREQ_BANDS * c]); + } + ddb_audio_data_t data; + data.fmt = &out_fmt; + data.data = freq_data; + data.nframes = DDB_FREQ_BANDS; + mutex_lock (wdl_mutex); + for (wavedata_listener_t *l = spectrum_listeners; l; l = l->next) { + l->callback (l->ctx, &data); } + mutex_unlock (wdl_mutex); + audio_data_fill = 0; } - mutex_unlock (wdl_mutex); - audio_data_fill = 0; - } - } while (remaining > 0); + } while (remaining > 0); + } } if (!output->has_volume) { @@ -2607,29 +2614,59 @@ streamer_notify_order_changed (int prev_order, int new_order) { } void -register_continuous_wavedata_listener (void *ctx, int type, void (*callback)(void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples)) { +vis_waveform_listen (void *ctx, void (*callback)(void *ctx, ddb_audio_data_t *data)) { + mutex_lock (wdl_mutex); + 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; + mutex_unlock (wdl_mutex); +} + +void +vis_waveform_unlisten (void *ctx) { + mutex_lock (wdl_mutex); + 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; + } + } + mutex_unlock (wdl_mutex); +} + +void +vis_spectrum_listen (void *ctx, void (*callback)(void *ctx, ddb_audio_data_t *data)) { mutex_lock (wdl_mutex); 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; + l->next = spectrum_listeners; + spectrum_listeners = l; mutex_unlock (wdl_mutex); } void -unregister_continuous_wavedata_listener (void *ctx, int type) { +vis_spectrum_unlisten (void *ctx) { mutex_lock (wdl_mutex); wavedata_listener_t *l, *prev = NULL; - for (l = wavedata_listeners; l; prev = l, l = l->next) { - if (l->ctx == ctx && l->type == type) { + for (l = spectrum_listeners; l; prev = l, l = l->next) { + if (l->ctx == ctx) { if (prev) { prev->next = l->next; } else { - wavedata_listeners = l->next; + spectrum_listeners = l->next; } free (l); break; @@ -149,9 +149,15 @@ struct handler_s * streamer_get_handler (void); void -register_continuous_wavedata_listener (void *ctx, int type, void (*callback)(void *ctx, int type, ddb_waveformat_t *fmt, const float *data, int nsamples)); +vis_waveform_listen (void *ctx, void (*callback)(void *ctx, ddb_audio_data_t *data)); void -unregister_continuous_wavedata_listener (void *ctx, int type); +vis_waveform_unlisten (void *ctx); + +void +vis_spectrum_listen (void *ctx, void (*callback)(void *ctx, ddb_audio_data_t *data)); + +void +vis_spectrum_unlisten (void *ctx); #endif // __STREAMER_H |