diff options
-rw-r--r-- | deadbeef.h | 9 | ||||
-rw-r--r-- | plugins.c | 1 | ||||
-rw-r--r-- | plugins/gtkui/gtkui.c | 1 | ||||
-rw-r--r-- | plugins/gtkui/widgets.c | 83 | ||||
-rw-r--r-- | plugins/gtkui/widgets.h | 2 | ||||
-rw-r--r-- | streamer.c | 34 | ||||
-rw-r--r-- | streamer.h | 3 |
7 files changed, 132 insertions, 1 deletions
@@ -307,6 +307,10 @@ enum ddb_sort_order_t { DDB_SORT_RANDOM, // available since API 1.3 }; +// audio memory constants +#define DDB_AUDIO_MEMORY_FRAMES 1000 +#define DDB_AUDIO_MEMORY_BUFFER_SIZE (DDB_AUDIO_MEMORY_FRAMES*4*20) + // typecasting macros #define DB_PLUGIN(x) ((DB_plugin_t *)(x)) #define DB_CALLBACK(x) ((DB_callback_t)(x)) @@ -758,6 +762,11 @@ typedef struct { // ******* new 1.3 APIs ******** int (*streamer_dsp_chain_save) (void); + // 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 at least DDB_AUDIO_MEMORY_BUFFER_SIZE + int (*audio_get_waveform_data) (ddb_waveformat_t *fmt, char *data); } DB_functions_t; // NOTE: an item placement must be selected like this @@ -331,6 +331,7 @@ static DB_functions_t deadbeef_api = { .pl_find_meta_raw = (const char *(*) (DB_playItem_t *it, const char *key))pl_find_meta_raw, // ******* new 1.3 APIs ******** .streamer_dsp_chain_save = streamer_dsp_chain_save, + .audio_get_waveform_data = audio_get_waveform_data, }; DB_functions_t *deadbeef = &deadbeef_api; diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index caab3be4..fc7700cf 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -1007,6 +1007,7 @@ gtkui_thread (void *ctx) { w_reg_widget ("playlist", _("Playlist"), w_playlist_create); w_reg_widget ("selproperties", _("Selection properties"), w_selproperties_create); w_reg_widget ("coverart", _("Album art display"), w_coverart_create); + w_reg_widget ("scope", _("Scope"), w_scope_create); mainwin = create_mainwin (); diff --git a/plugins/gtkui/widgets.c b/plugins/gtkui/widgets.c index e787304c..c584c555 100644 --- a/plugins/gtkui/widgets.c +++ b/plugins/gtkui/widgets.c @@ -86,6 +86,12 @@ typedef struct { GtkWidget *drawarea; } w_coverart_t; +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *drawarea; + guint drawtimer; +} w_scope_t; + static int design_mode; static ddb_gtkui_widget_t *rootwidget; @@ -181,7 +187,6 @@ w_init_cb (void *data) { void w_replace (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *from, ddb_gtkui_widget_t *to) { - printf ("replace to %s\n", to->type); if (w->replace) { w->replace (w, from, to); if (to->init) { @@ -1685,3 +1690,79 @@ w_coverart_create (void) { w_override_signals (w->base.widget, w); return (ddb_gtkui_widget_t *)w; } + +///// scope vis +void +w_scope_destroy (ddb_gtkui_widget_t *w) { + w_scope_t *s = (w_scope_t *)w; + if (s->drawtimer) { + g_source_remove (s->drawtimer); + } +} + +gboolean +w_scope_draw_cb (void *data) { + w_scope_t *s = data; + gtk_widget_queue_draw (s->drawarea); + return TRUE; +} + +gboolean +scope_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + ddb_waveformat_t fmt; + char data[DDB_AUDIO_MEMORY_BUFFER_SIZE]; + int size = deadbeef->audio_get_waveform_data (&fmt, data); + if (fmt.channels && size > 0) { + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + cairo_set_line_width (cr, 1); + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + short *samples = (short *)data; + + int nframes = size / (fmt.bps/8*fmt.channels); + float incr = nframes / (float)a.width; + float pos = 0; + for (int x = 0; x < a.width; x++, pos += incr) { + short s = max (samples[(int)pos*2], samples[(int)pos*2+1]); + cairo_line_to (cr, x, s * a.height/2 / 0x7fff + a.height/2); + } + cairo_stroke (cr); + } + + return FALSE; +} + +gboolean +scope_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = scope_draw (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +void +w_scope_init (ddb_gtkui_widget_t *w) { + w_scope_t *s = (w_scope_t *)w; + s->drawtimer = g_timeout_add (33, w_scope_draw_cb, w); +} + +ddb_gtkui_widget_t * +w_scope_create (void) { + w_scope_t *w = malloc (sizeof (w_scope_t)); + memset (w, 0, sizeof (w_scope_t)); + + w->base.widget = gtk_event_box_new (); + w->base.init = w_scope_init; + w->base.destroy = w_scope_destroy; + w->drawarea = gtk_drawing_area_new (); + gtk_widget_show (w->drawarea); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (scope_expose_event), w); +#else + g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (scope_draw), w); +#endif + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t *)w; +} diff --git a/plugins/gtkui/widgets.h b/plugins/gtkui/widgets.h index 93bdfd48..27e8c013 100644 --- a/plugins/gtkui/widgets.h +++ b/plugins/gtkui/widgets.h @@ -96,4 +96,6 @@ w_selproperties_create (void); ddb_gtkui_widget_t * w_coverart_create (void); +ddb_gtkui_widget_t * +w_scope_create (void); #endif @@ -90,6 +90,7 @@ 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; @@ -119,6 +120,10 @@ static int streamer_buffering; // to allow interruption of stall file requests static DB_FILE *streamer_file; +// for vis plugins +static char audio_data[DDB_AUDIO_MEMORY_BUFFER_SIZE]; +static ddb_waveformat_t audio_fmt; + #if DETECT_PL_LOCK_RC volatile pthread_t streamer_lock_tid = 0; #endif @@ -1693,6 +1698,7 @@ streamer_init (void) { #endif mutex = mutex_create (); decodemutex = mutex_create (); + audio_mem_mutex = mutex_create (); ringbuf_init (&streamer_ringbuf, streambuffer, STREAM_BUFFER_SIZE); @@ -1734,6 +1740,8 @@ streamer_free (void) { decodemutex = 0; mutex_free (mutex); mutex = 0; + mutex_free (audio_mem_mutex); + audio_mem_mutex = 0; streamer_dsp_chain_save(); @@ -1953,6 +1961,7 @@ streamer_read (char *bytes, int size) { if (formatchanged && bytes_until_next_song <= 0) { streamer_set_output_format (); formatchanged = 0; + memset (audio_data, 0, sizeof (audio_data)); } streamer_lock (); int sz = min (size, streamer_ringbuf.remaining); @@ -2002,6 +2011,18 @@ streamer_read (char *bytes, int size) { printf ("streamer_read took %d ms\n", ms); #endif + mutex_lock (audio_mem_mutex); + int mem_size = DDB_AUDIO_MEMORY_FRAMES * (output->fmt.bps >> 3) * output->fmt.channels; + if (sz < mem_size) { + memmove (audio_data, audio_data + mem_size - sz, sz); + memcpy (audio_data + mem_size - sz, bytes, sz); + } + else { + memcpy (audio_data, bytes + sz - mem_size, mem_size); + } + memcpy (&audio_fmt, &output->fmt, sizeof (ddb_waveformat_t)); + mutex_unlock (audio_mem_mutex); + if (!output->has_volume) { char *stream = bytes; int bytesread = sz; @@ -2251,3 +2272,16 @@ streamer_notify_order_changed (int prev_order, int new_order) { streamer_unlock (); } } + +int +audio_get_waveform_data (ddb_waveformat_t *fmt, char *data) { + if (!audio_mem_mutex) { + return -1; + } + mutex_lock (audio_mem_mutex); + memcpy (fmt, &audio_fmt, sizeof (ddb_waveformat_t)); + int mem_size = DDB_AUDIO_MEMORY_FRAMES * (audio_fmt.bps >> 3) * audio_fmt.channels; + memcpy (data, audio_data, mem_size); + mutex_unlock (audio_mem_mutex); + return mem_size; +} @@ -130,4 +130,7 @@ streamer_dsp_chain_save (void); void streamer_notify_order_changed (int prev_order, int new_order); +int +audio_get_waveform_data (ddb_waveformat_t *fmt, char *data); + #endif // __STREAMER_H |