summaryrefslogtreecommitdiffstats
path: root/gst
diff options
context:
space:
mode:
Diffstat (limited to 'gst')
-rw-r--r--gst/playondemand/Makefile.am5
-rw-r--r--gst/playondemand/demo-mp3.c134
-rw-r--r--gst/playondemand/filter.func70
-rw-r--r--gst/playondemand/gstplayondemand.c367
-rw-r--r--gst/playondemand/gstplayondemand.h42
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 */