diff options
Diffstat (limited to 'gst/playondemand/gstplayondemand.c')
-rw-r--r-- | gst/playondemand/gstplayondemand.c | 367 |
1 files changed, 286 insertions, 81 deletions
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()); |