diff options
-rw-r--r-- | deadbeef.h | 7 | ||||
-rw-r--r-- | plugins.c | 2 | ||||
-rw-r--r-- | plugins/gtkui/widgets.c | 63 | ||||
-rw-r--r-- | streamer.c | 45 | ||||
-rw-r--r-- | streamer.h | 6 |
5 files changed, 118 insertions, 5 deletions
@@ -824,8 +824,15 @@ typedef struct { // 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); + // this is useful to mute/unmute audio, and query the muted status, from // plugins, without touching the volume control void (*audio_set_mute) (int mute); @@ -342,6 +342,8 @@ static DB_functions_t deadbeef_api = { .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, .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 88c0f8f7..e42601b6 100644 --- a/plugins/gtkui/widgets.c +++ b/plugins/gtkui/widgets.c @@ -134,6 +134,8 @@ typedef struct { #if USE_OPENGL GdkGLContext *glcontext; #endif + float *samples; + int nsamples; } w_scope_t; typedef struct { @@ -2233,6 +2235,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); if (s->drawtimer) { g_source_remove (s->drawtimer); s->drawtimer = 0; @@ -2243,6 +2246,10 @@ w_scope_destroy (ddb_gtkui_widget_t *w) { s->glcontext = NULL; } #endif + if (s->samples) { + free (s->samples); + s->samples = NULL; + } } gboolean @@ -2252,11 +2259,27 @@ w_scope_draw_cb (void *data) { return TRUE; } +static void +scope_wavedata_listener (void *ctx, ddb_waveformat_t *fmt, const float *data, int in_samples) { + w_scope_t *w = ctx; + if (w->samples) { + // append + float ratio = (44100.f / fmt->samplerate); + int size = in_samples * ratio; + + int sz = min (w->nsamples, size); + int n = w->nsamples-sz; + + memmove (w->samples, w->samples + sz, n * sizeof (float)); + float pos = 0; + for (int i = 0; i < sz && pos < in_samples; i++, pos += 1./ratio) { + w->samples[n + i] = data[(int)(pos * fmt->channels)]; + } + } +} + gboolean scope_draw_cairo (GtkWidget *widget, cairo_t *cr, gpointer user_data) { - ddb_waveformat_t fmt; - float data[DDB_AUDIO_MEMORY_FRAMES]; - deadbeef->audio_get_waveform_data (DDB_AUDIO_WAVEFORM, data); GtkAllocation a; gtk_widget_get_allocation (widget, &a); cairo_set_source_rgb (cr, 0, 0, 0); @@ -2266,12 +2289,45 @@ scope_draw_cairo (GtkWidget *widget, cairo_t *cr, gpointer user_data) { cairo_set_line_width (cr, 1); cairo_set_source_rgb (cr, 1, 1, 1); + w_scope_t *w = user_data; + + float spp = 1; + int nsamples = a.width / spp; + if (w->nsamples != nsamples) { + printf ("init\n"); + float *oldsamples = w->samples; + int oldnsamples = w->nsamples; + w->samples = NULL; + w->nsamples = nsamples; + if (nsamples > 0) { + w->samples = malloc (sizeof (float) * nsamples); + memset (w->samples, 0, sizeof (float) * nsamples); + if (oldsamples) { + int n = min (oldnsamples, w->nsamples); + memcpy (w->samples + w->nsamples - n, oldsamples + oldnsamples - n, n * sizeof (float)); + free (oldsamples); + } + } + else { + return FALSE; + } + } + + + float incr = a.width / (float)w->nsamples; + for (int i = 0; i < w->nsamples; i++) { + float s = w->samples[i]; + cairo_line_to (cr, i, s * a.height/2 + a.height/2); + } + +#if 0 float incr = a.width / (float)DDB_AUDIO_MEMORY_FRAMES; float pos = 0; for (float x = 0; x < a.width && pos < DDB_AUDIO_MEMORY_FRAMES; x += incr, pos ++) { float s = data[(int)pos]; cairo_line_to (cr, x, s * a.height/2 + a.height/2); } +#endif cairo_stroke (cr); return FALSE; @@ -2398,6 +2454,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); return (ddb_gtkui_widget_t *)w; } @@ -142,6 +142,15 @@ static float audio_data[DDB_AUDIO_MEMORY_FRAMES]; // message queue 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); + struct wavedata_listener_s *next; +} wavedata_listener_t; + +static wavedata_listener_t *wavedata_listeners; + #if DETECT_PL_LOCK_RC volatile pthread_t streamer_lock_tid = 0; #endif @@ -2281,12 +2290,19 @@ streamer_read (char *bytes, int size) { .is_float = 1, .is_bigendian = 0 }; + + 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)); - pcm_convert (&output->fmt, bytes, &out_fmt, (char *)(audio_data + DDB_AUDIO_MEMORY_FRAMES - in_frames), sz); + memcpy (audio_data + DDB_AUDIO_MEMORY_FRAMES - in_frames, temp_audio_data, sz); } else { - pcm_convert (&output->fmt, bytes + sz - DDB_AUDIO_MEMORY_FRAMES * in_frame_size, &out_fmt, (char *)audio_data, DDB_AUDIO_MEMORY_FRAMES * in_frame_size); + 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); @@ -2576,6 +2592,31 @@ audio_get_waveform_data (int type, float *data) { } void +register_continuous_wavedata_listener (void *ctx, void (*callback)(void *ctx, 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->callback = callback; + l->next = wavedata_listeners; + wavedata_listeners = l; +} + +void +unregister_continuous_wavedata_listener (void *ctx) { + wavedata_listener_t *l, *prev = NULL; + for (l = wavedata_listeners; l && l->ctx != ctx; prev = l, l = l->next); + if (l) { + if (prev) { + prev->next = l->next; + } + else { + wavedata_listeners = l->next; + } + free (l); + } +} + +void streamer_set_streamer_playlist (playlist_t *plt) { if (streamer_playlist) { plt_unref (streamer_playlist); @@ -148,4 +148,10 @@ streamer_set_streamer_playlist (playlist_t *plt); 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)); + +void +unregister_continuous_wavedata_listener (void *ctx); + #endif // __STREAMER_H |