summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--deadbeef.h7
-rw-r--r--plugins.c2
-rw-r--r--plugins/gtkui/widgets.c63
-rw-r--r--streamer.c45
-rw-r--r--streamer.h6
5 files changed, 118 insertions, 5 deletions
diff --git a/deadbeef.h b/deadbeef.h
index 5dc12615..1ea892ab 100644
--- a/deadbeef.h
+++ b/deadbeef.h
@@ -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);
diff --git a/plugins.c b/plugins.c
index 51d2a594..8b6d3ff9 100644
--- a/plugins.c
+++ b/plugins.c
@@ -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;
}
diff --git a/streamer.c b/streamer.c
index 89137d47..1348b617 100644
--- a/streamer.c
+++ b/streamer.c
@@ -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);
diff --git a/streamer.h b/streamer.h
index fc37dc2c..96acab9a 100644
--- a/streamer.h
+++ b/streamer.h
@@ -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