diff options
Diffstat (limited to 'gst')
-rw-r--r-- | gst/playondemand/Makefile.am | 5 | ||||
-rw-r--r-- | gst/playondemand/demo-mp3.c | 134 | ||||
-rw-r--r-- | gst/playondemand/filter.func | 70 | ||||
-rw-r--r-- | gst/playondemand/gstplayondemand.c | 367 | ||||
-rw-r--r-- | gst/playondemand/gstplayondemand.h | 42 |
5 files changed, 458 insertions, 160 deletions
diff --git a/gst/playondemand/Makefile.am b/gst/playondemand/Makefile.am index 63052ac1..43f12140 100644 --- a/gst/playondemand/Makefile.am +++ b/gst/playondemand/Makefile.am @@ -13,8 +13,7 @@ if HAVE_GTK noinst_PROGRAMS = demo_mp3 endif -demo_mp3_SOURCES = demo-mp3.c +demo_mp3_SOURCES = demo-mp3.c gstplayondemand.h ## putting GTK_CFLAGS first fixes a weird compilation error with GTK and XML demo_mp3_CFLAGS = $(GTK_CFLAGS) $(GST_CFLAGS) -demo_mp3_LDFLAGS = $(GST_LIBS) $(GTK_LIBS) - +demo_mp3_LDFLAGS = $(GST_LIBS) $(GTK_LIBS) ./libgstplayondemand.la diff --git a/gst/playondemand/demo-mp3.c b/gst/playondemand/demo-mp3.c index 91b9e3bf..2bb2005c 100644 --- a/gst/playondemand/demo-mp3.c +++ b/gst/playondemand/demo-mp3.c @@ -3,80 +3,140 @@ #include <gtk/gtk.h> #include <gst/gst.h> -void play (GtkButton *button, gpointer data) +#include "gstplayondemand.h" + +guint channels; +GtkWidget *window, *vbox, *play_button, *reset_button, *quit_button; +GtkWidget *hbox, *measure1_button, *measure2_button, *measure3_button, \ + *measure4_button, *measure5_button, *measure6_button, *speed_scale; +GstElement *src, *mad, *pod, *osssink, *pipeline; +GstClock *element_clock; + +void +play (GtkButton *button, gpointer data) { - g_signal_emit_by_name(G_OBJECT(data), "play", NULL, NULL); + g_signal_emit_by_name(G_OBJECT(pod), "play", NULL, NULL); } - -void reset (GtkButton *button, gpointer data) + +void +reset (GtkButton *button, gpointer data) { - g_signal_emit_by_name(G_OBJECT(data), "reset", NULL, NULL); + g_signal_emit_by_name(G_OBJECT(pod), "reset", NULL, NULL); } - -int main(int argc, char **argv) + +void +measure (GtkToggleButton *button, gpointer data) +{ + gst_play_on_demand_toggle_beat(GST_PLAYONDEMAND(pod), + GPOINTER_TO_UINT(data), 0); +} + +void +speed (GtkAdjustment *scale, gpointer data) +{ + gst_clock_set_speed(element_clock, gtk_adjustment_get_value(scale)); +} + +void +setup_pipeline (gchar *filename) { - guint channels; - GtkWidget *window, *vbox, *play_button, *reset_button, *quit_button; - GstElement *src, *mad, *pod, *osssink, *pipeline; - - gst_init (&argc, &argv); - gtk_init (&argc, &argv); - - if (argc!=2) { - g_print("usage: %s <mp3-filename>\n", argv[0]); - exit(-1); - } - src = gst_element_factory_make("filesrc", "filesrc"); mad = gst_element_factory_make("mad", "mad"); pod = gst_element_factory_make("playondemand", "playondemand"); osssink = gst_element_factory_make("osssink", "osssink"); - g_object_set(G_OBJECT(src), "location", argv[1], NULL); + g_object_set(G_OBJECT(src), "location", filename, NULL); + g_object_set(G_OBJECT(pod), "silent", FALSE, NULL); g_object_set(G_OBJECT(osssink), "fragment", 0x00180008, NULL); g_object_get(G_OBJECT(osssink), "channels", &channels, NULL); pipeline = gst_pipeline_new("app"); - gst_bin_add(GST_BIN(pipeline), src); - gst_bin_add(GST_BIN(pipeline), mad); - gst_bin_add(GST_BIN(pipeline), pod); - gst_bin_add(GST_BIN(pipeline), osssink); - - gst_element_connect(src, mad); - gst_element_connect(pod, osssink); - gst_element_connect(mad, pod); + gst_bin_add_many(GST_BIN(pipeline), src, mad, pod, osssink, NULL); + gst_element_connect_many(src, mad, pod, osssink, NULL); - gst_element_set_state(pipeline, GST_STATE_PLAYING); + element_clock = gst_bin_get_clock(GST_BIN(pipeline)); + gst_element_set_clock(GST_ELEMENT(pod), element_clock); + /* gst_clock_set_speed(element_clock, 0.00001); */ +} +void +setup_gui (void) +{ /* initialize gui elements ... */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - vbox = gtk_vbox_new(FALSE, 0); + vbox = gtk_vbox_new(TRUE, 0); + hbox = gtk_hbox_new(TRUE, 0); play_button = gtk_button_new_with_label("play"); reset_button = gtk_button_new_with_label("reset"); quit_button = gtk_button_new_with_label("quit"); + measure1_button = gtk_toggle_button_new_with_label("one"); + measure2_button = gtk_toggle_button_new_with_label("two"); + measure3_button = gtk_toggle_button_new_with_label("three"); + measure4_button = gtk_toggle_button_new_with_label("four"); + measure5_button = gtk_toggle_button_new_with_label("five"); + measure6_button = gtk_toggle_button_new_with_label("six"); + speed_scale = gtk_hscale_new_with_range(0.0, 0.001, 0.000001); + /* gtk_adjustment_set_value(GTK_ADJUSTMENT(speed_scale), 0.00001); */ /* do the packing stuff ... */ gtk_window_set_default_size(GTK_WINDOW(window), 96, 96); gtk_container_add(GTK_CONTAINER(window), vbox); - gtk_box_pack_start(GTK_BOX(vbox), play_button, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(vbox), reset_button, FALSE, FALSE, 2); - gtk_box_pack_start(GTK_BOX(vbox), quit_button, FALSE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(vbox), play_button, TRUE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(vbox), reset_button, TRUE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 2); + gtk_box_pack_start(GTK_BOX(hbox), measure1_button, TRUE, TRUE, 2); + gtk_box_pack_start(GTK_BOX(hbox), measure2_button, TRUE, TRUE, 2); + gtk_box_pack_start(GTK_BOX(hbox), measure3_button, TRUE, TRUE, 2); + gtk_box_pack_start(GTK_BOX(hbox), measure4_button, TRUE, TRUE, 2); + gtk_box_pack_start(GTK_BOX(hbox), measure5_button, TRUE, TRUE, 2); + gtk_box_pack_start(GTK_BOX(hbox), measure6_button, TRUE, TRUE, 2); + /*gtk_box_pack_start(GTK_BOX(vbox), speed_scale, TRUE, FALSE, 2);*/ + gtk_box_pack_start(GTK_BOX(vbox), quit_button, TRUE, FALSE, 2); /* connect things ... */ - g_signal_connect(G_OBJECT(play_button), "clicked", G_CALLBACK(play), pod); - g_signal_connect(G_OBJECT(reset_button), "clicked", G_CALLBACK(reset), pod); + g_signal_connect(G_OBJECT(play_button), "clicked", G_CALLBACK(play), NULL); + g_signal_connect(G_OBJECT(reset_button), "clicked", G_CALLBACK(reset), NULL); g_signal_connect(G_OBJECT(quit_button), "clicked", gtk_main_quit, NULL); + g_signal_connect(G_OBJECT(measure1_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(0)); + g_signal_connect(G_OBJECT(measure2_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(1)); + g_signal_connect(G_OBJECT(measure3_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(2)); + g_signal_connect(G_OBJECT(measure4_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(3)); + g_signal_connect(G_OBJECT(measure5_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(4)); + g_signal_connect(G_OBJECT(measure6_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(5)); + /*g_signal_connect(G_OBJECT(speed_scale), "value-changed", G_CALLBACK(speed), NULL);*/ /* show the gui. */ gtk_widget_show(play_button); gtk_widget_show(reset_button); gtk_widget_show(quit_button); + gtk_widget_show(measure1_button); + gtk_widget_show(measure2_button); + gtk_widget_show(measure3_button); + gtk_widget_show(measure4_button); + gtk_widget_show(measure5_button); + gtk_widget_show(measure6_button); + gtk_widget_show(hbox); + /*gtk_widget_show(speed_scale);*/ gtk_widget_show(vbox); gtk_widget_show(window); gtk_idle_add((GtkFunction)gst_bin_iterate, pipeline); - - gtk_main(); +} + +int +main(int argc, char **argv) +{ + gst_init (&argc, &argv); + gtk_init (&argc, &argv); + + if (argc!=2) { + g_print("usage: %s <mp3-filename>\n", argv[0]); + exit(-1); + } + setup_pipeline(argv[1]); + gst_element_set_state(pipeline, GST_STATE_PLAYING); + setup_gui(); + gtk_main(); return 0; } diff --git a/gst/playondemand/filter.func b/gst/playondemand/filter.func index 091626b2..2d77189f 100644 --- a/gst/playondemand/filter.func +++ b/gst/playondemand/filter.func @@ -4,6 +4,7 @@ _TYPE_ *data_in, *data_out, *filter_data; filter_data = (_TYPE_ *) filter->buffer; num_filter = filter->buffer_size / sizeof(_TYPE_); +max_filter = (filter->play_from_beginning) ? num_filter : G_MAXUINT; /******************************************************************************/ /* see if we've got any events coming through ... */ @@ -21,7 +22,7 @@ do { in = gst_pad_pull(filter->sinkpad); } - /******************************************************************************/ + /****************************************************************************/ /* first handle data from the input buffer. */ GST_DEBUG(0, "--- done with events, going to input"); @@ -34,44 +35,47 @@ do { w = filter->write; /* copy the input data to the filter's internal buffer. */ - if (filter->follow_stream_tail) { - for (j = 0; j < num_in; j++) { - filter_data[(w + j) % num_filter] = data_in[j]; - } + for (j = 0; (j < num_in) && ((w + j) < max_filter); j++) { + filter_data[(w + j) % num_filter] = data_in[j]; + } - filter->write = (w + j) % num_filter; - - /* update the start pointer */ - if ((filter->start != 0) || ((w + j) >= num_filter)) { - filter->start = (filter->write + 1) % num_filter; - } - } else { - for (j = 0; (j < num_in) && ((w + j) < num_filter); j++) { - filter_data[w + j] = data_in[j]; - } + filter->write = (w + j) % num_filter; - filter->write += j; + if ((w + j) >= num_filter) { + filter->buffer_filled_once = TRUE; - /* if we're not following the stream tail, the buffer is just a straight - buffer. so we need to set eos if we've passed the limit of the internal - buffer size. */ - if ((w + j) >= num_filter) { + /* if we're not playing from the end of the stream, the buffer is not a + ring buffer, so it has a fixed size. we need to set eos here because + we've passed that limit. */ + if (filter->play_from_beginning) { filter->eos = TRUE; } } + /* update the start pointer */ + if ((! filter->play_from_beginning) && filter->buffer_filled_once) { + filter->start = (filter->write + 1) % num_filter; + } + out = in; } else { - j = 0; + j = num_filter; w = 0; - + out = gst_buffer_new_from_pool(filter->bufpool, 0, 0); } - /******************************************************************************/ + /****************************************************************************/ + /* check to see if we have to add a new play pointer. */ + + GST_DEBUG(0, "--- done with input, checking clock before output"); + + play_on_demand_update_plays_from_clock(filter); + + /****************************************************************************/ /* now handle output data. */ - GST_DEBUG(0, "--- done with input, going to output"); + GST_DEBUG(0, "--- done with clock, going to output"); data_out = (_TYPE_ *) GST_BUFFER_DATA(out); num_out = GST_BUFFER_SIZE(out) / sizeof(_TYPE_); @@ -79,30 +83,33 @@ do { for (k = 0; k < num_out; k++) { data_out[k] = zero; } - + /* output play pointer data. */ - for (t = 0; t < POD_MAX_PLAYS; t++) { + for (t = 0; t < GST_POD_MAX_PLAY_PTRS; t++) { offset = filter->plays[t]; if (offset != G_MAXUINT) { - if (filter->follow_stream_tail) { + if (! filter->play_from_beginning) { for (k = 0; k < num_out; k++) { data_out[k] = CLAMP(data_out[k] + filter_data[(offset + k) % num_filter], min, max); } } else { - for (k = 0; (k < num_out) && ((offset + k) < (w + j)); k++) { + for (k = 0; (k < num_out) && (k < (w + j - offset)); k++) { data_out[k] = CLAMP(data_out[k] + filter_data[offset + k], min, max); } } - if ((offset < w) && ((offset + k) >= (w + j))) { - filter->plays[t] = G_MAXUINT; + if ((! filter->play_from_beginning) || ((offset + k) < (w + j))) { + filter->plays[t] = (offset + k) % num_filter; } else { - filter->plays[t] = (filter->plays[t] + k) % num_filter; + filter->plays[t] = G_MAXUINT; } } } + /****************************************************************************/ + /* push out the buffer. */ + GST_DEBUG(0, "--- done with output, pushing buffer %p", out); gst_pad_push(filter->srcpad, out); @@ -110,6 +117,7 @@ do { if (! filter->eos) { in = gst_pad_pull(filter->sinkpad); } + gst_element_yield (GST_ELEMENT (filter)); } while (TRUE); diff --git a/gst/playondemand/gstplayondemand.c b/gst/playondemand/gstplayondemand.c index 45814382..0e8266b2 100644 --- a/gst/playondemand/gstplayondemand.c +++ b/gst/playondemand/gstplayondemand.c @@ -23,23 +23,25 @@ #include "gstplayondemand.h" -#define POD_MAX_PLAYS 192 /* maximum number of simultaneous plays */ -#define POD_BUFPOOL_SIZE 4096 /* gstreamer buffer size to make if no +#define GST_POD_MAX_PLAY_PTRS 128 /* maximum number of simultaneous plays */ +#define GST_POD_NUM_MEASURES 8 /* default number of measures */ +#define GST_POD_NUM_BEATS 16 /* default number of beats in a measure */ +#define GST_POD_BUFPOOL_SIZE 4096 /* gstreamer buffer size to use if no bufferpool is available, must be divisible by sizeof(gfloat) */ -#define POD_BUFPOOL_NUM 6 /* number of buffers to allocate per chunk in +#define GST_POD_BUFPOOL_NUM 6 /* number of buffers to allocate per chunk in sink buffer pool */ -#define POD_BUFFER_SIZE 882000 /* enough space for 10 seconds of 16-bit audio - at 44100 samples per second ... */ +#define GST_POD_BUFFER_SIZE 882000 /* enough space for 5 seconds of 32-bit float + audio at 44100 samples per second ... */ /* elementfactory information */ static GstElementDetails play_on_demand_details = { "Play On Demand", "Filter/Audio/Effect", "LGPL", - "Plays a stream whenever it receives a certain signal", + "Plays a stream at specific times, or when it receives a signal", VERSION, - "Leif Morgan Johnson <lmjohns3@eos.ncsu.edu>", + "Leif Morgan Johnson <leif@ambient.2y.net>", "(C) 2001", }; @@ -55,58 +57,71 @@ enum { static guint gst_pod_filter_signals[LAST_SIGNAL] = { 0 }; enum { - ARG_0, - ARG_SILENT, - ARG_FOLLOWTAIL, - ARG_BUFFERSIZE + PROP_0, + PROP_SILENT, + PROP_PLAYFROMBEGINNING, + PROP_BUFFERSIZE, + PROP_NUM_BEATS, + PROP_NUM_MEASURES }; -static GstPadTemplate* -play_on_demand_sink_factory (void) -{ - static GstPadTemplate *template = NULL; - - if (!template) { - template = gst_pad_template_new +static GstPadTemplate* +play_on_demand_sink_factory (void) +{ + static GstPadTemplate *template = NULL; + + if (!template) { + template = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, gst_caps_append(gst_caps_new ("sink_int", "audio/raw", GST_AUDIO_INT_PAD_TEMPLATE_PROPS), gst_caps_new ("sink_float", "audio/raw", GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS)), NULL); - } - return template; + } + return template; } static GstPadTemplate* play_on_demand_src_factory (void) { static GstPadTemplate *template = NULL; - + if (!template) - template = gst_pad_template_new + template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, gst_caps_append (gst_caps_new ("src_float", "audio/raw", GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS), gst_caps_new ("src_int", "audio/raw", GST_AUDIO_INT_PAD_TEMPLATE_PROPS)), NULL); - + return template; } -static void play_on_demand_class_init (GstPlayOnDemandClass *klass); -static void play_on_demand_init (GstPlayOnDemand *filter); +static void play_on_demand_class_init (GstPlayOnDemandClass *klass); +static void play_on_demand_init (GstPlayOnDemand *filter); + +static void play_on_demand_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void play_on_demand_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); -static void play_on_demand_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); -static void play_on_demand_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static GstPadConnectReturn play_on_demand_pad_connect (GstPad *pad, GstCaps *caps); -static GstPadConnectReturn play_on_demand_pad_connect (GstPad *pad, GstCaps *caps); +static void play_on_demand_loop (GstElement *elem); -static void play_on_demand_loop (GstElement *elem); +static void play_on_demand_set_clock (GstElement *elem, GstClock *clock); -static void play_on_demand_play_handler (GstElement *elem); -static void play_on_demand_reset_handler (GstElement *elem); +static void play_on_demand_play_handler (GstElement *elem); +static void play_on_demand_add_play_ptr (GstPlayOnDemand *filter, guint pos); +static void play_on_demand_reset_handler (GstElement *elem); + +static void play_on_demand_update_plays_from_clock (GstPlayOnDemand *filter); static GstElementClass *parent_class = NULL; @@ -125,17 +140,17 @@ play_on_demand_pad_connect (GstPad *pad, GstCaps *caps) { const gchar *format; GstPlayOnDemand *filter; - + g_return_val_if_fail(caps != NULL, GST_PAD_CONNECT_DELAYED); g_return_val_if_fail(pad != NULL, GST_PAD_CONNECT_DELAYED); filter = GST_PLAYONDEMAND(GST_PAD_PARENT(pad)); gst_caps_get_string(caps, "format", &format); - + gst_caps_get_int(caps, "rate", &filter->rate); gst_caps_get_int(caps, "channels", &filter->channels); - + if (strcmp(format, "int") == 0) { filter->format = GST_PLAYONDEMAND_FORMAT_INT; gst_caps_get_int (caps, "width", &filter->width); @@ -145,23 +160,30 @@ play_on_demand_pad_connect (GstPad *pad, GstCaps *caps) gst_caps_get_boolean (caps, "signed", &filter->is_signed); if (!filter->silent) { - g_print ("PlayOnDemand : channels %d, rate %d\n", + g_print ("PlayOnDemand : channels %d, rate %d\n", filter->channels, filter->rate); g_print ("PlayOnDemand : format int, bit width %d, endianness %d, signed %s\n", filter->width, filter->endianness, filter->is_signed ? "yes" : "no"); } - } else if (strcmp(format, "float") == 0) { + + filter->buffer_samples = filter->buffer_size; + filter->buffer_samples /= (filter->width) ? filter->width / 8 : 1; + filter->buffer_samples /= (filter->channels) ? filter->channels : 1; +} else if (strcmp(format, "float") == 0) { filter->format = GST_PLAYONDEMAND_FORMAT_FLOAT; gst_caps_get_string (caps, "layout", &filter->layout); gst_caps_get_float (caps, "intercept", &filter->intercept); gst_caps_get_float (caps, "slope", &filter->slope); if (!filter->silent) { - g_print ("PlayOnDemand : channels %d, rate %d\n", + g_print ("PlayOnDemand : channels %d, rate %d\n", filter->channels, filter->rate); g_print ("PlayOnDemand : format float, layout %s, intercept %f, slope %f\n", filter->layout, filter->intercept, filter->slope); } + + filter->buffer_samples = filter->buffer_size / sizeof(gfloat); + filter->buffer_samples /= (filter->channels) ? filter->channels : 1; } if (GST_CAPS_IS_FIXED (caps)) @@ -186,7 +208,9 @@ gst_play_on_demand_get_type (void) 0, (GInstanceInitFunc) play_on_demand_init, }; - play_on_demand_type = g_type_register_static(GST_TYPE_ELEMENT, "GstPlayOnDemand", &play_on_demand_info, 0); + play_on_demand_type = g_type_register_static(GST_TYPE_ELEMENT, + "GstPlayOnDemand", + &play_on_demand_info, 0); } return play_on_demand_type; } @@ -223,17 +247,25 @@ play_on_demand_class_init (GstPlayOnDemandClass *klass) parent_class = g_type_class_ref(GST_TYPE_ELEMENT); - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SILENT, + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_SILENT, g_param_spec_boolean("silent","silent","silent", TRUE, G_PARAM_READWRITE)); - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FOLLOWTAIL, - g_param_spec_boolean("follow-stream-tail","follow-stream-tail","follow-stream-tail", - FALSE, G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_PLAYFROMBEGINNING, + g_param_spec_boolean("play-from-beginning","play-from-beginning","play-from-beginning", + TRUE, G_PARAM_READWRITE)); - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFFERSIZE, + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_BUFFERSIZE, g_param_spec_uint("buffer-size","buffer-size","buffer-size", - 0, G_MAXUINT - 1, POD_BUFFER_SIZE, G_PARAM_READWRITE)); + 0, G_MAXUINT - 1, GST_POD_BUFFER_SIZE, G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_NUM_BEATS, + g_param_spec_uint("num-beats","num-beats","num-beats", + 0, G_MAXUINT - 1, GST_POD_NUM_BEATS, G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_NUM_MEASURES, + g_param_spec_uint("num-measures","num-measures","num-measures", + 0, G_MAXUINT - 1, GST_POD_NUM_MEASURES, G_PARAM_READWRITE)); gobject_class->set_property = play_on_demand_set_property; gobject_class->get_property = play_on_demand_get_property; @@ -243,32 +275,44 @@ static void play_on_demand_init (GstPlayOnDemand *filter) { guint i; - + filter->srcpad = gst_pad_new_from_template(play_on_demand_src_factory(), "src"); filter->sinkpad = gst_pad_new_from_template(play_on_demand_sink_factory(), "sink"); gst_pad_set_bufferpool_function(filter->sinkpad, play_on_demand_get_bufferpool); gst_pad_set_connect_function(filter->sinkpad, play_on_demand_pad_connect); - + gst_element_add_pad(GST_ELEMENT(filter), filter->sinkpad); gst_element_add_pad(GST_ELEMENT(filter), filter->srcpad); gst_element_set_loop_function(GST_ELEMENT(filter), play_on_demand_loop); - filter->follow_stream_tail = FALSE; - filter->silent = TRUE; - - filter->buffer = g_new(gchar, POD_BUFFER_SIZE); - filter->buffer_size = POD_BUFFER_SIZE; + filter->buffer = g_new(gchar, GST_POD_BUFFER_SIZE); + filter->buffer_size = GST_POD_BUFFER_SIZE; filter->start = 0; filter->write = 0; - filter->eos = FALSE; + filter->eos = FALSE; + filter->buffer_filled_once = FALSE; + filter->play_from_beginning = TRUE; + filter->silent = TRUE; + + GST_ELEMENT (filter)->setclockfunc = play_on_demand_set_clock; + filter->clock = NULL; + filter->last_time = 0; + + filter->num_beats = GST_POD_NUM_BEATS; + filter->num_measures = GST_POD_NUM_MEASURES; + filter->total_beats = filter->num_beats * filter->num_measures; + filter->times = g_new(guint64, filter->num_measures); + for (i = 0; i < filter->num_measures; i++) { + filter->times[i] = 0; + } /* the plays are stored as an array of buffer offsets. this initializes the - array to `blank' values (G_MAXUINT is an invalid index for this filter). */ - filter->plays = g_new(guint, POD_MAX_PLAYS); - for (i = 0; i < POD_MAX_PLAYS; i++) { + array to `blank' values (G_MAXUINT is the `invalid' index). */ + filter->plays = g_new(guint, GST_POD_MAX_PLAY_PTRS); + for (i = 0; i < GST_POD_MAX_PLAY_PTRS; i++) { filter->plays[i] = G_MAXUINT; } } @@ -277,18 +321,19 @@ static void play_on_demand_loop (GstElement *elem) { GstPlayOnDemand *filter = GST_PLAYONDEMAND(elem); - guint num_in, num_out, num_filter; + guint num_in, num_out, num_filter, max_filter; GstBuffer *in, *out; register guint j, k, t; guint w, offset; - + g_return_if_fail(filter != NULL); g_return_if_fail(GST_IS_PLAYONDEMAND(filter)); filter->bufpool = gst_pad_get_bufferpool(filter->srcpad); if (filter->bufpool == NULL) { - filter->bufpool = gst_buffer_pool_get_default(POD_BUFPOOL_SIZE, POD_BUFPOOL_NUM); + filter->bufpool = gst_buffer_pool_get_default(GST_POD_BUFPOOL_SIZE, + GST_POD_BUFPOOL_NUM); } in = gst_pad_pull(filter->sinkpad); @@ -320,15 +365,37 @@ play_on_demand_loop (GstElement *elem) } static void -play_on_demand_play_handler(GstElement *elem) +play_on_demand_set_clock (GstElement *elem, GstClock *clock) +{ + GstPlayOnDemand *filter; + g_return_if_fail(GST_IS_PLAYONDEMAND(elem)); + filter = GST_PLAYONDEMAND(elem); + g_return_if_fail(filter != NULL); + + filter->clock = clock; +} + +static void +play_on_demand_play_handler (GstElement *elem) +{ + GstPlayOnDemand *filter; + + g_return_if_fail(GST_IS_PLAYONDEMAND(elem)); + filter = GST_PLAYONDEMAND(elem); + g_return_if_fail(filter != NULL); + + play_on_demand_add_play_ptr(filter, filter->start); +} + +static void +play_on_demand_add_play_ptr (GstPlayOnDemand *filter, guint pos) { - GstPlayOnDemand *filter = GST_PLAYONDEMAND(elem); register guint i; - for (i = 0; i < POD_MAX_PLAYS; i++) { + for (i = 0; i < GST_POD_MAX_PLAY_PTRS; i++) { if (filter->plays[i] == G_MAXUINT) { - filter->plays[i] = filter->start; - break; + filter->plays[i] = pos; + return; } } } @@ -336,42 +403,178 @@ play_on_demand_play_handler(GstElement *elem) static void play_on_demand_reset_handler(GstElement *elem) { - GstPlayOnDemand *filter = GST_PLAYONDEMAND(elem); + GstPlayOnDemand *filter; register guint i; - - for (i = 0; i < POD_MAX_PLAYS; i++) { + + g_return_if_fail(GST_IS_PLAYONDEMAND(elem)); + filter = GST_PLAYONDEMAND(elem); + g_return_if_fail(filter != NULL); + + for (i = 0; i < GST_POD_MAX_PLAY_PTRS; i++) { filter->plays[i] = G_MAXUINT; } filter->start = 0; filter->write = 0; + filter->eos = FALSE; + filter->buffer_filled_once = FALSE; + + for (i = 0; i < filter->num_measures; i++) { + filter->times[i] = 0; + } } +#define GST_POD_SAMPLE_OFFSET(f, dt) (((f)->start - ((dt) / (f)->rate)) % (f)->buffer_samples) + static void -play_on_demand_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +play_on_demand_update_plays_from_clock(GstPlayOnDemand *filter) +{ + register guint t; + guint total, beats, last, time; + + g_return_if_fail(GST_IS_PLAYONDEMAND(filter)); + g_return_if_fail(filter != NULL); + + if (filter->clock) { + total = filter->total_beats; + beats = filter->num_beats; + + last = filter->last_time; + time = (guint) ((gst_clock_get_time(filter->clock) / 10000000LL) % total); + filter->last_time = time; + + GST_DEBUG(0, "--- clock time %u, last %u, total %u", time, last, total); + + /* if the current time is less than the last time, the clock has wrapped + around the total number of beats ... we need to count back to 0 and then + wrap around to the end. */ + if (time < last) { + for (t = time; t != G_MAXUINT; t--) { + if (filter->times[t / beats] & ((guint64) 1 << (t % beats))) { + play_on_demand_add_play_ptr(filter, + GST_POD_SAMPLE_OFFSET(filter, time - t)); + } + } + + time = total - 1; + } + + for (t = time; t > last; t--) { + if (filter->times[t / beats] & ((guint64) 1 << (t % beats))) { + play_on_demand_add_play_ptr(filter, + GST_POD_SAMPLE_OFFSET(filter, time - t)); + } + } + } +} + +void +gst_play_on_demand_set_beat(GstPlayOnDemand *filter, const guint measure, + const guint beat, const gboolean value) +{ + g_return_if_fail(GST_IS_PLAYONDEMAND(filter)); + g_return_if_fail(filter != NULL); + g_return_if_fail(filter->num_measures > measure); + g_return_if_fail(filter->total_beats < (filter->num_beats * measure + beat)); + + if (value) { + filter->times[measure] |= (1 << beat); + } else { + filter->times[measure] &= (((guint64) -1) ^ (1 << beat)); + } +} + +gboolean +gst_play_on_demand_get_beat(GstPlayOnDemand *filter, const guint measure, + const guint beat) +{ + g_return_val_if_fail(GST_IS_PLAYONDEMAND(filter), FALSE); + g_return_val_if_fail(filter != NULL, FALSE); + g_return_val_if_fail(filter->num_measures > measure, FALSE); + g_return_val_if_fail(filter->total_beats > + (filter->num_beats * measure + beat), FALSE); + + return ((filter->times[measure] >> beat) & ((guint64) 1)); +} + +void +gst_play_on_demand_toggle_beat(GstPlayOnDemand *filter, const guint measure, + const guint beat) +{ + g_return_if_fail(GST_IS_PLAYONDEMAND(filter)); + g_return_if_fail(filter != NULL); + g_return_if_fail(filter->num_measures > measure); + g_return_if_fail(filter->total_beats > (filter->num_beats * measure + beat)); + + filter->times[measure] ^= (1 << beat); +} + +static void +play_on_demand_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) { GstPlayOnDemand *filter; + register guchar c; + register guint i; + gchar *new_buffer; + guint64 *new_measures; /* it's not null if we got it, but it might not be ours */ g_return_if_fail(GST_IS_PLAYONDEMAND(object)); filter = GST_PLAYONDEMAND(object); + g_return_if_fail(filter != NULL); switch (prop_id) { - case ARG_BUFFERSIZE: + case PROP_BUFFERSIZE: filter->buffer_size = g_value_get_uint(value); - /* reallocate space for the buffer with the new size values. */ + if (filter->format == GST_PLAYONDEMAND_FORMAT_FLOAT) { + filter->buffer_samples = filter->buffer_size \ + / sizeof(gfloat) / filter->channels; + } else { + filter->buffer_samples = filter->buffer_size \ + / filter->width / filter->channels; + } + + /* allocate space for a new buffer, copy old data, remove invalid play + pointers. */ + new_buffer = g_new(gchar, filter->buffer_size); + for (c = 0; c < filter->buffer_size; c++) { + new_buffer[c] = filter->buffer[c]; + } + g_free(filter->buffer); - filter->buffer = g_new(gchar, filter->buffer_size); + filter->buffer = new_buffer; - /* reset the play pointers and read/write indexes. */ - play_on_demand_reset_handler(GST_ELEMENT(filter)); + for (i = 0; i < GST_POD_MAX_PLAY_PTRS; i++) { + if (filter->plays[i] > filter->buffer_size) { + filter->plays[i] = G_MAXUINT; + } + } + break; + case PROP_NUM_BEATS: + filter->num_beats = g_value_get_uint(value); + filter->total_beats = filter->num_measures * filter->num_beats; break; - case ARG_SILENT: + case PROP_NUM_MEASURES: + filter->num_measures = g_value_get_uint(value); + filter->total_beats = filter->num_measures * filter->num_beats; + + /* reallocate space for beat information, copy old data. this will remove + measures at the end if the number of measures shrinks. */ + new_measures = g_new(guint64, filter->num_measures); + for (i = 0; i < filter->num_measures; i++) { + new_measures[i] = filter->times[i]; + } + + g_free(filter->times); + filter->times = new_measures; + break; + case PROP_SILENT: filter->silent = g_value_get_boolean(value); break; - case ARG_FOLLOWTAIL: - filter->follow_stream_tail = g_value_get_boolean(value); + case PROP_PLAYFROMBEGINNING: + filter->play_from_beginning = g_value_get_boolean(value); play_on_demand_reset_handler(GST_ELEMENT(filter)); break; default: @@ -380,23 +583,25 @@ play_on_demand_set_property (GObject *object, guint prop_id, const GValue *value } static void -play_on_demand_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +play_on_demand_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) { GstPlayOnDemand *filter; /* it's not null if we got it, but it might not be ours */ g_return_if_fail(GST_IS_PLAYONDEMAND(object)); filter = GST_PLAYONDEMAND(object); + g_return_if_fail(filter != NULL); switch (prop_id) { - case ARG_BUFFERSIZE: + case PROP_BUFFERSIZE: g_value_set_uint(value, filter->buffer_size); break; - case ARG_SILENT: + case PROP_SILENT: g_value_set_boolean(value, filter->silent); break; - case ARG_FOLLOWTAIL: - g_value_set_boolean(value, filter->follow_stream_tail); + case PROP_PLAYFROMBEGINNING: + g_value_set_boolean(value, filter->play_from_beginning); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -413,7 +618,7 @@ plugin_init (GModule *module, GstPlugin *plugin) GST_TYPE_PLAYONDEMAND, &play_on_demand_details); g_return_val_if_fail(factory != NULL, FALSE); - + gst_element_factory_add_pad_template(factory, play_on_demand_src_factory()); gst_element_factory_add_pad_template(factory, play_on_demand_sink_factory()); diff --git a/gst/playondemand/gstplayondemand.h b/gst/playondemand/gstplayondemand.h index 57dd189d..d34c0c6f 100644 --- a/gst/playondemand/gstplayondemand.h +++ b/gst/playondemand/gstplayondemand.h @@ -25,7 +25,6 @@ #include <config.h> #include <gst/gst.h> -/* #include <gst/meta/audioraw.h> */ #ifdef __cplusplus @@ -65,30 +64,46 @@ struct _GstPlayOnDemand { size. */ gchar *buffer; guint buffer_size; + guint buffer_samples; + guint *plays; guint write; guint start; - guint *plays; + gboolean play_from_beginning; + gboolean buffer_filled_once; gboolean eos; - - gboolean follow_stream_tail; - gboolean silent; - + + /* the playondemand filter needs to keep track of a number of 'measures' + consisting of 'beats'. these are represented as an array of guint64s, with + each guint64 being one measure, and the bits in each measure being beats + (lower order bits come first). each measure can therefore have a maximum of + 64 beats, though there are a potentially unlimited number of measures. + + this is basically a way to figure out when incoming clock signals should + add a play pointer. */ + GstClock *clock; + guint last_time; + + guint64 *times; + guint num_measures; + guint num_beats; + guint total_beats; + /* the next three are valid for both int and float */ GstPlayOnDemandFormat format; guint rate; guint channels; - + /* the next five are valid only for format == GST_PLAYONDEMAND_FORMAT_INT */ guint width; guint depth; guint endianness; guint law; gboolean is_signed; - + /* the next three are valid only for format == GST_PLAYONDEMAND_FORMAT_FLOAT */ const gchar *layout; gfloat slope; @@ -104,6 +119,17 @@ struct _GstPlayOnDemandClass { GType gst_play_on_demand_get_type(void); +void gst_play_on_demand_set_beat (GstPlayOnDemand *filter, + const guint measure, + const guint beat, + const gboolean value); +gboolean gst_play_on_demand_get_beat (GstPlayOnDemand *filter, + const guint measure, + const guint beat); +void gst_play_on_demand_toggle_beat (GstPlayOnDemand *filter, + const guint measure, + const guint beat); + #ifdef __cplusplus } #endif /* __cplusplus */ |