diff options
author | Leif Johnson <leif@ambient.2y.net> | 2002-10-17 20:05:58 +0000 |
---|---|---|
committer | Leif Johnson <leif@ambient.2y.net> | 2002-10-17 20:05:58 +0000 |
commit | ce94093ed4c095a478fd99700be79a2dd55afc22 (patch) | |
tree | b2a2910a724e4fa4e596fd13f9083df8a0e54239 /gst/playondemand/gstplayondemand.c | |
parent | 4754e4e8ccbdafd14e97b846f343500214060b99 (diff) |
Added measures and beats to the playondemand filter so it can act like an audio sequencer. Currently defines three ex...
Original commit message from CVS:
Added measures and beats to the playondemand filter so it can act like an audio
sequencer. Currently defines three extra globally visible functions, might
eventually want to put them in an interface instead ?
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 458143826..0e8266b29 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()); |