+ modifying playondemand to behave a little more like a sequencer

Original commit message from CVS:
+ modifying playondemand to behave a little more like a sequencer
This commit is contained in:
Leif Johnson 2003-01-17 14:04:39 +00:00
parent b2d148685a
commit 6b785348c4
5 changed files with 471 additions and 531 deletions

View file

@ -1,2 +1 @@
demo-mp3
demo_mp3 demo_mp3

View file

@ -3,14 +3,25 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <gst/gst.h> #include <gst/gst.h>
#include "gstplayondemand.h" #define NUM_BEATS 16
#define SPEED 1e-9
guint channels; GtkWidget *window, *vbox, *beat_box, *button_box;
GtkWidget *window, *vbox, *play_button, *reset_button, *quit_button; GtkWidget *play_button, *clear_button, *reset_button, *quit_button;
GtkWidget *hbox, *measure1_button, *measure2_button, *measure3_button, \ GtkWidget **beat_button;
*measure4_button, *measure5_button, *measure6_button, *speed_scale; GtkWidget *speed_scale;
GtkObject *speed_adj;
GstElement *src, *mad, *pod, *osssink, *pipeline; GstElement *src, *mad, *pod, *osssink, *pipeline;
GstClock *element_clock; GstClock *element_clock;
GSList *beats;
void
played (GstElement *pod, gpointer data)
{
g_print("Played beat at %u\n",
((guint) (gst_clock_get_time(element_clock) *
(GTK_ADJUSTMENT(speed_adj))->value * SPEED)) % NUM_BEATS);
}
void void
play (GtkButton *button, gpointer data) play (GtkButton *button, gpointer data)
@ -18,23 +29,36 @@ play (GtkButton *button, gpointer data)
g_signal_emit_by_name(G_OBJECT(pod), "play", NULL, NULL); g_signal_emit_by_name(G_OBJECT(pod), "play", NULL, NULL);
} }
void
clear (GtkButton *button, gpointer data)
{
g_signal_emit_by_name(G_OBJECT(pod), "clear", NULL, NULL);
}
void void
reset (GtkButton *button, gpointer data) reset (GtkButton *button, gpointer data)
{ {
guint i;
g_signal_emit_by_name(G_OBJECT(pod), "reset", NULL, NULL); g_signal_emit_by_name(G_OBJECT(pod), "reset", NULL, NULL);
for (i = 0; i < NUM_BEATS; i++)
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(beat_button[i]), 0);
} }
void void
measure (GtkToggleButton *button, gpointer data) beat (GtkToggleButton *button, gpointer data)
{ {
gst_play_on_demand_toggle_beat(GST_PLAYONDEMAND(pod), if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
GPOINTER_TO_UINT(data), 0); beats = g_slist_append(beats, data);
else
beats = g_slist_remove(beats, data);
g_object_set(G_OBJECT(pod), "tick-list", beats, NULL);
} }
void void
speed (GtkAdjustment *scale, gpointer data) speed (GtkAdjustment *adjustment, gpointer data)
{ {
gst_clock_set_speed(element_clock, gtk_adjustment_get_value(scale)); g_object_set(G_OBJECT(pod), "clock-speed", adjustment->value * SPEED, NULL);
/*gst_clock_set_speed(element_clock, adjustment->value * SPEED);*/
} }
void void
@ -46,9 +70,9 @@ setup_pipeline (gchar *filename)
osssink = gst_element_factory_make("osssink", "osssink"); osssink = gst_element_factory_make("osssink", "osssink");
g_object_set(G_OBJECT(src), "location", filename, 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_set(G_OBJECT(osssink), "fragment", 0x00180008, NULL);
g_object_get(G_OBJECT(osssink), "channels", &channels, NULL); g_object_set(G_OBJECT(pod), "total-ticks", NUM_BEATS,
"clock-speed", SPEED, NULL);
pipeline = gst_pipeline_new("app"); pipeline = gst_pipeline_new("app");
@ -57,69 +81,79 @@ setup_pipeline (gchar *filename)
element_clock = gst_bin_get_clock(GST_BIN(pipeline)); element_clock = gst_bin_get_clock(GST_BIN(pipeline));
gst_element_set_clock(GST_ELEMENT(pod), element_clock); gst_element_set_clock(GST_ELEMENT(pod), element_clock);
/* gst_clock_set_speed(element_clock, 0.00001); */
} }
void void
setup_gui (void) setup_gui (void)
{ {
guint i;
beat_button = g_new(GtkWidget *, NUM_BEATS);
/* initialize gui elements ... */ /* initialize gui elements ... */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL); window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(window), 12);
vbox = gtk_vbox_new(TRUE, 0); vbox = gtk_vbox_new(TRUE, 0);
hbox = gtk_hbox_new(TRUE, 0); gtk_box_set_spacing(GTK_BOX(vbox), 12);
play_button = gtk_button_new_with_label("play");
reset_button = gtk_button_new_with_label("reset"); beat_box = gtk_hbox_new(TRUE, 0);
quit_button = gtk_button_new_with_label("quit"); button_box = gtk_hbox_new(TRUE, 0);
measure1_button = gtk_toggle_button_new_with_label("one");
measure2_button = gtk_toggle_button_new_with_label("two"); play_button = gtk_button_new_with_label("Play");
measure3_button = gtk_toggle_button_new_with_label("three"); clear_button = gtk_button_new_with_label("Reset Sound");
measure4_button = gtk_toggle_button_new_with_label("four"); reset_button = gtk_button_new_with_label("Reset All");
measure5_button = gtk_toggle_button_new_with_label("five"); quit_button = gtk_button_new_with_label("Quit");
measure6_button = gtk_toggle_button_new_with_label("six");
speed_scale = gtk_hscale_new_with_range(0.0, 0.001, 0.000001); for (i = 0; i < NUM_BEATS; i++)
/* gtk_adjustment_set_value(GTK_ADJUSTMENT(speed_scale), 0.00001); */ beat_button[i] = gtk_toggle_button_new_with_label(g_strdup_printf("%2d", i));
speed_adj = gtk_adjustment_new(1, 0.0, 2, 0.01, 0.1, 0.0);
speed_scale = gtk_hscale_new(GTK_ADJUSTMENT(speed_adj));
gtk_scale_set_digits(GTK_SCALE(speed_scale), 4);
gtk_range_set_update_policy(GTK_RANGE(speed_scale), GTK_UPDATE_DISCONTINUOUS);
/* do the packing stuff ... */ /* do the packing stuff ... */
gtk_window_set_default_size(GTK_WINDOW(window), 96, 96); gtk_window_set_default_size(GTK_WINDOW(window), 96, 96);
gtk_container_add(GTK_CONTAINER(window), vbox); gtk_container_add(GTK_CONTAINER(window), vbox);
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(button_box), play_button, TRUE, TRUE, 2);
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, FALSE, 2); gtk_box_pack_start(GTK_BOX(button_box), clear_button, TRUE, TRUE, 2);
gtk_box_pack_start(GTK_BOX(hbox), measure1_button, TRUE, TRUE, 2); gtk_box_pack_start(GTK_BOX(button_box), reset_button, TRUE, TRUE, 2);
gtk_box_pack_start(GTK_BOX(hbox), measure2_button, TRUE, TRUE, 2); gtk_box_pack_start(GTK_BOX(button_box), quit_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); for (i = 0; i < NUM_BEATS; i++)
gtk_box_pack_start(GTK_BOX(hbox), measure5_button, TRUE, TRUE, 2); gtk_box_pack_start(GTK_BOX(beat_box), beat_button[i], 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), button_box, TRUE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), quit_button, TRUE, FALSE, 2); gtk_box_pack_start(GTK_BOX(vbox), beat_box, TRUE, FALSE, 2);
gtk_box_pack_start(GTK_BOX(vbox), speed_scale, TRUE, FALSE, 2);
/* connect things ... */ /* connect things ... */
g_signal_connect(G_OBJECT(play_button), "clicked", G_CALLBACK(play), NULL); g_signal_connect(G_OBJECT(play_button), "clicked", G_CALLBACK(play), NULL);
g_signal_connect(G_OBJECT(clear_button), "clicked", G_CALLBACK(clear), NULL);
g_signal_connect(G_OBJECT(reset_button), "clicked", G_CALLBACK(reset), 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(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(pod), "played", G_CALLBACK(played), NULL);
g_signal_connect(G_OBJECT(measure2_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(1)); g_signal_connect(G_OBJECT(speed_adj), "value_changed", G_CALLBACK(speed), NULL);
g_signal_connect(G_OBJECT(measure3_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(2)); for (i = 0; i < NUM_BEATS; i++)
g_signal_connect(G_OBJECT(measure4_button), "toggled", G_CALLBACK(measure), GUINT_TO_POINTER(3)); g_signal_connect(G_OBJECT(beat_button[i]), "toggled", G_CALLBACK(beat), GUINT_TO_POINTER(i));
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. */ /* show the gui. */
gtk_widget_show(play_button); gtk_widget_show(play_button);
gtk_widget_show(clear_button);
gtk_widget_show(reset_button); gtk_widget_show(reset_button);
gtk_widget_show(quit_button); gtk_widget_show(quit_button);
gtk_widget_show(measure1_button);
gtk_widget_show(measure2_button); for (i = 0; i < NUM_BEATS; i++)
gtk_widget_show(measure3_button); gtk_widget_show(beat_button[i]);
gtk_widget_show(measure4_button);
gtk_widget_show(measure5_button); gtk_widget_show(beat_box);
gtk_widget_show(measure6_button); gtk_widget_show(button_box);
gtk_widget_show(hbox); gtk_widget_show(speed_scale);
/*gtk_widget_show(speed_scale);*/
gtk_widget_show(vbox); gtk_widget_show(vbox);
gtk_widget_show(window); gtk_widget_show(window);
gtk_idle_add((GtkFunction)gst_bin_iterate, pipeline); gtk_idle_add((GtkFunction)gst_bin_iterate, pipeline);
} }
@ -138,5 +172,6 @@ main(int argc, char **argv)
gst_element_set_state(pipeline, GST_STATE_PLAYING); gst_element_set_state(pipeline, GST_STATE_PLAYING);
setup_gui(); setup_gui();
gtk_main(); gtk_main();
g_free(beat_button);
return 0; return 0;
} }

View file

@ -3,18 +3,19 @@
_TYPE_ *data_in, *data_out, *filter_data; _TYPE_ *data_in, *data_out, *filter_data;
filter_data = (_TYPE_ *) filter->buffer; filter_data = (_TYPE_ *) filter->buffer;
num_filter = filter->buffer_size / sizeof(_TYPE_); num_filter = filter->buffer_bytes / sizeof(_TYPE_);
max_filter = (filter->play_from_beginning) ? num_filter : G_MAXUINT;
/******************************************************************************/ /******************************************************************************/
/* see if we've got any events coming through ... */ /* see if we've got any events coming through ... */
do { do {
GST_DEBUG(0, "--- going to events"); while (GST_IS_EVENT(in)) {
while (! filter->eos && GST_IS_EVENT(in)) {
if (GST_EVENT_TYPE(in) == GST_EVENT_EOS) { if (GST_EVENT_TYPE(in) == GST_EVENT_EOS) {
filter->eos = TRUE; filter->eos = TRUE;
} else if ((GST_EVENT_TYPE(in) == GST_EVENT_SEEK) ||
(GST_EVENT_TYPE(in) == GST_EVENT_FLUSH)) {
filter->eos = FALSE;
filter->write = 0;
} else { } else {
gst_pad_push(filter->srcpad, in); gst_pad_push(filter->srcpad, in);
} }
@ -25,8 +26,6 @@ do {
/****************************************************************************/ /****************************************************************************/
/* first handle data from the input buffer. */ /* first handle data from the input buffer. */
GST_DEBUG(0, "--- done with events, going to input");
/* only update the input if there hasn't been an eos yet. */ /* only update the input if there hasn't been an eos yet. */
if (! filter->eos) { if (! filter->eos) {
data_in = (_TYPE_ *) GST_BUFFER_DATA(in); data_in = (_TYPE_ *) GST_BUFFER_DATA(in);
@ -35,27 +34,13 @@ do {
w = filter->write; w = filter->write;
/* copy the input data to the filter's internal buffer. */ /* copy the input data to the filter's internal buffer. */
for (j = 0; (j < num_in) && ((w + j) < max_filter); j++) { for (j = 0; (j < num_in) && ((w + j) < num_filter); j++)
filter_data[(w + j) % num_filter] = data_in[j]; filter_data[(w + j) % num_filter] = data_in[j];
}
filter->write = (w + j) % num_filter; filter->write = (w + j) % num_filter;
if ((w + j) >= num_filter) { if ((w + j) >= num_filter)
filter->buffer_filled_once = TRUE; filter->eos = TRUE;
/* 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; out = in;
} else { } else {
@ -68,56 +53,58 @@ do {
/****************************************************************************/ /****************************************************************************/
/* check to see if we have to add a new play pointer. */ /* check to see if we have to add a new play pointer. */
GST_DEBUG(0, "--- done with input, checking clock before output"); if (filter->clock) {
current_tick = ((guint) (gst_clock_get_time(filter->clock) *
filter->clock_speed)) % filter->total_ticks;
play_on_demand_update_plays_from_clock(filter); if (current_tick != last_tick) {
/* now we go through the tick list and play samples */
/****************************************************************************/ tick_list = filter->tick_list;
/* now handle output data. */ while (tick_list) {
tick = GPOINTER_TO_UINT(tick_list->data);
GST_DEBUG(0, "--- done with clock, going to output"); if (current_tick == tick)
play_on_demand_add_play_pointer(filter, 0);
data_out = (_TYPE_ *) GST_BUFFER_DATA(out); else if (GST_POD_TICK_ELAPSED(tick, current_tick, last_tick))
num_out = GST_BUFFER_SIZE(out) / sizeof(_TYPE_); play_on_demand_add_play_pointer(filter, GST_POD_SAMPLE_OFFSET(filter, current_tick - tick));
tick_list = g_slist_next(tick_list);
for (k = 0; k < num_out; k++) {
data_out[k] = zero;
}
/* output play pointer data. */
for (t = 0; t < GST_POD_MAX_PLAY_PTRS; t++) {
offset = filter->plays[t];
if (offset != G_MAXUINT) {
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) && (k < (w + j - offset)); k++) {
data_out[k] = CLAMP(data_out[k] + filter_data[offset + k], min, max);
}
}
if ((! filter->play_from_beginning) || ((offset + k) < (w + j))) {
filter->plays[t] = (offset + k) % num_filter;
} else {
filter->plays[t] = G_MAXUINT;
} }
last_tick = current_tick;
} }
} }
/****************************************************************************/ /****************************************************************************/
/* push out the buffer. */ /* now handle output data. */
GST_DEBUG(0, "--- done with output, pushing buffer %p", out); data_out = (_TYPE_ *) GST_BUFFER_DATA(out);
num_out = GST_BUFFER_SIZE(out) / sizeof(_TYPE_);
for (k = 0; k < num_out; k++)
data_out[k] = zero;
/* output play pointer data. */
if (! filter->mute)
for (t = 0; t < filter->max_plays; t++) {
offset = filter->plays[t];
if (offset != G_MAXUINT) {
for (k = 0; (k < num_out) && (offset + k < num_filter); k++)
data_out[k] = CLAMP(data_out[k] + filter_data[offset + k], min, max);
if ((offset + k) == num_filter)
filter->plays[t] = G_MAXUINT;
else
filter->plays[t] = offset + k;
}
}
/****************************************************************************/
/* push out the buffer. */
gst_pad_push(filter->srcpad, out); gst_pad_push(filter->srcpad, out);
if (! filter->eos) { if (! filter->eos)
in = gst_pad_pull(filter->sinkpad); in = gst_pad_pull(filter->sinkpad);
}
gst_element_yield (GST_ELEMENT (filter)); gst_element_interrupt (GST_ELEMENT (filter));
} while (TRUE); } while (TRUE);

View file

@ -17,24 +17,25 @@
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*/ */
#include <string.h> #include <string.h>
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/audio/audio.h> #include <gst/audio/audio.h>
#include "gstplayondemand.h" #include "gstplayondemand.h"
#define GST_POD_MAX_PLAY_PTRS 128 /* maximum number of simultaneous plays */ /* some default values */
#define GST_POD_NUM_MEASURES 8 /* default number of measures */ #define GST_POD_MAX_PLAYS 100 /* maximum simultaneous plays */
#define GST_POD_NUM_BEATS 16 /* default number of beats in a measure */ #define GST_POD_BUFFER_TIME 5.0 /* buffer length in seconds */
#define GST_POD_BUFPOOL_SIZE 4096 /* gstreamer buffer size to use if no #define GST_POD_CLOCK_SPEED 1e-8 /* 0.1 sec/tick default */
bufferpool is available, must be divisible
by sizeof(gfloat) */
#define GST_POD_BUFPOOL_NUM 6 /* number of buffers to allocate per chunk in
sink buffer pool */
#define GST_POD_BUFFER_SIZE 882000 /* enough space for 5 seconds of 32-bit float
audio at 44100 samples per second ... */
/* elementfactory information */ /* buffer pool fallback values ... use if no buffer pool is available */
#define GST_POD_BUFPOOL_SIZE 4096
#define GST_POD_BUFPOOL_NUM 6
/* element factory information */
static GstElementDetails play_on_demand_details = { static GstElementDetails play_on_demand_details = {
"Play On Demand", "Play On Demand",
"Filter/Audio/Effect", "Filter/Audio/Effect",
@ -42,29 +43,10 @@ static GstElementDetails play_on_demand_details = {
"Plays a stream at specific times, or when it receives a signal", "Plays a stream at specific times, or when it receives a signal",
VERSION, VERSION,
"Leif Morgan Johnson <leif@ambient.2y.net>", "Leif Morgan Johnson <leif@ambient.2y.net>",
"(C) 2001", "(C) 2002",
}; };
/* Filter signals and args */
enum {
/* FILL ME */
PLAY_SIGNAL,
RESET_SIGNAL,
LAST_SIGNAL
};
static guint gst_pod_filter_signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
PROP_SILENT,
PROP_PLAYFROMBEGINNING,
PROP_BUFFERSIZE,
PROP_NUM_BEATS,
PROP_NUM_MEASURES
};
static GstPadTemplate* static GstPadTemplate*
play_on_demand_sink_factory (void) play_on_demand_sink_factory (void)
{ {
@ -73,15 +55,16 @@ play_on_demand_sink_factory (void)
if (!template) { if (!template) {
template = gst_pad_template_new template = gst_pad_template_new
("sink", GST_PAD_SINK, GST_PAD_ALWAYS, ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
gst_caps_append(gst_caps_new ("sink_int", "audio/raw", gst_caps_append(gst_caps_new ("sink_int", "audio/raw",
GST_AUDIO_INT_PAD_TEMPLATE_PROPS), GST_AUDIO_INT_PAD_TEMPLATE_PROPS),
gst_caps_new ("sink_float", "audio/raw", gst_caps_new ("sink_float", "audio/raw",
GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS)), GST_AUDIO_FLOAT_MONO_PAD_TEMPLATE_PROPS)),
NULL); NULL);
} }
return template; return template;
} }
static GstPadTemplate* static GstPadTemplate*
play_on_demand_src_factory (void) play_on_demand_src_factory (void)
{ {
@ -99,97 +82,27 @@ play_on_demand_src_factory (void)
return template; return template;
} }
/* GObject functionality */
static void play_on_demand_class_init (GstPlayOnDemandClass *klass); static void play_on_demand_class_init (GstPlayOnDemandClass *klass);
static void play_on_demand_init (GstPlayOnDemand *filter); 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, /* GStreamer functionality */
guint prop_id, static GstBufferPool* play_on_demand_get_bufferpool (GstPad *pad);
const GValue *value, static GstPadLinkReturn play_on_demand_pad_link (GstPad *pad, GstCaps *caps);
GParamSpec *pspec); static void play_on_demand_loop (GstElement *elem);
static void play_on_demand_get_property (GObject *object, static void play_on_demand_set_clock (GstElement *elem, GstClock *clock);
guint prop_id,
GValue *value,
GParamSpec *pspec);
static GstPadLinkReturn play_on_demand_pad_connect (GstPad *pad, GstCaps *caps);
static void play_on_demand_loop (GstElement *elem);
static void play_on_demand_set_clock (GstElement *elem, GstClock *clock);
/* signal handlers */
static void play_on_demand_play_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_clear_handler (GstElement *elem);
static void play_on_demand_reset_handler (GstElement *elem); static void play_on_demand_reset_handler (GstElement *elem);
static void play_on_demand_update_plays_from_clock (GstPlayOnDemand *filter); /* utility functions */
static void play_on_demand_add_play_pointer (GstPlayOnDemand *filter, guint pos);
static GstElementClass *parent_class = NULL; static void play_on_demand_resize_buffer (GstPlayOnDemand *filter);
static GstBufferPool*
play_on_demand_get_bufferpool (GstPad *pad)
{
GstPlayOnDemand *filter;
filter = GST_PLAYONDEMAND(gst_pad_get_parent(pad));
return gst_pad_get_bufferpool(filter->srcpad);
}
static GstPadLinkReturn
play_on_demand_pad_connect (GstPad *pad, GstCaps *caps)
{
const gchar *format;
GstPlayOnDemand *filter;
g_return_val_if_fail(caps != NULL, GST_PAD_LINK_DELAYED);
g_return_val_if_fail(pad != NULL, GST_PAD_LINK_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);
gst_caps_get_int (caps, "depth", &filter->depth);
gst_caps_get_int (caps, "law", &filter->law);
gst_caps_get_int (caps, "endianness", &filter->endianness);
gst_caps_get_boolean (caps, "signed", &filter->is_signed);
if (!filter->silent) {
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");
}
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",
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))
return gst_pad_try_set_caps (filter->srcpad, caps);
return GST_PAD_LINK_DELAYED;
}
GType GType
gst_play_on_demand_get_type (void) gst_play_on_demand_get_type (void)
@ -215,6 +128,31 @@ gst_play_on_demand_get_type (void)
return play_on_demand_type; return play_on_demand_type;
} }
/* signals and properties */
enum {
/* add signals here */
PLAYED_SIGNAL,
PLAY_SIGNAL,
CLEAR_SIGNAL,
RESET_SIGNAL,
LAST_SIGNAL
};
enum {
PROP_0,
PROP_MUTE,
PROP_BUFFER_TIME,
PROP_MAX_PLAYS,
PROP_CLOCK_SPEED,
PROP_TOTAL_TICKS,
PROP_TICK_LIST,
};
static guint gst_pod_filter_signals[LAST_SIGNAL] = { 0 };
static GstElementClass *parent_class = NULL;
static void static void
play_on_demand_class_init (GstPlayOnDemandClass *klass) play_on_demand_class_init (GstPlayOnDemandClass *klass)
{ {
@ -224,6 +162,15 @@ play_on_demand_class_init (GstPlayOnDemandClass *klass)
gobject_class = (GObjectClass *) klass; gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass; gstelement_class = (GstElementClass *) klass;
gst_pod_filter_signals[PLAYED_SIGNAL] =
g_signal_new("played",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GstPlayOnDemandClass, played),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
gst_pod_filter_signals[PLAY_SIGNAL] = gst_pod_filter_signals[PLAY_SIGNAL] =
g_signal_new("play", g_signal_new("play",
G_TYPE_FROM_CLASS(klass), G_TYPE_FROM_CLASS(klass),
@ -233,6 +180,15 @@ play_on_demand_class_init (GstPlayOnDemandClass *klass)
g_cclosure_marshal_VOID__VOID, g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0); G_TYPE_NONE, 0);
gst_pod_filter_signals[CLEAR_SIGNAL] =
g_signal_new("clear",
G_TYPE_FROM_CLASS(klass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GstPlayOnDemandClass, clear),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
gst_pod_filter_signals[RESET_SIGNAL] = gst_pod_filter_signals[RESET_SIGNAL] =
g_signal_new("reset", g_signal_new("reset",
G_TYPE_FROM_CLASS(klass), G_TYPE_FROM_CLASS(klass),
@ -243,34 +199,45 @@ play_on_demand_class_init (GstPlayOnDemandClass *klass)
G_TYPE_NONE, 0); G_TYPE_NONE, 0);
klass->play = play_on_demand_play_handler; klass->play = play_on_demand_play_handler;
klass->clear = play_on_demand_clear_handler;
klass->reset = play_on_demand_reset_handler; klass->reset = play_on_demand_reset_handler;
parent_class = g_type_class_ref(GST_TYPE_ELEMENT); parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
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), 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), PROP_BUFFERSIZE,
g_param_spec_uint("buffer-size","buffer-size","buffer-size",
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->set_property = play_on_demand_set_property;
gobject_class->get_property = play_on_demand_get_property; gobject_class->get_property = play_on_demand_get_property;
gstelement_class->set_clock = play_on_demand_set_clock; gstelement_class->set_clock = play_on_demand_set_clock;
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_MUTE,
g_param_spec_boolean("mute", "Silence output",
"Do not output any sound",
FALSE, G_PARAM_READWRITE));
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_BUFFER_TIME,
g_param_spec_float("buffer-time", "Buffer length in seconds",
"Number of seconds of audio the buffer holds",
0.0, G_MAXUINT - 2, GST_POD_BUFFER_TIME, G_PARAM_READWRITE));
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_MAX_PLAYS,
g_param_spec_uint("plays", "Maximum simultaneous playback",
"Maximum allowed number of simultaneous plays from the buffer",
1, G_MAXUINT, GST_POD_MAX_PLAYS, G_PARAM_READWRITE));
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_CLOCK_SPEED,
g_param_spec_float("clock-speed", "Clock speed (ticks/second)",
"The relative speed of a musical tick",
0, G_MAXFLOAT, 1, G_PARAM_READWRITE));
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_TOTAL_TICKS,
g_param_spec_uint("total-ticks", "Total number of ticks",
"Total number of ticks (only relevant for tick lists)",
1, G_MAXUINT, 1, G_PARAM_READWRITE));
g_object_class_install_property(G_OBJECT_CLASS(klass), PROP_TICK_LIST,
g_param_spec_pointer("tick-list", "List of ticks to play",
"A list of ticks (musical times) at which to play the sample",
G_PARAM_READWRITE));
} }
static void static void
@ -282,51 +249,184 @@ play_on_demand_init (GstPlayOnDemand *filter)
filter->sinkpad = gst_pad_new_from_template(play_on_demand_sink_factory(), "sink"); 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_bufferpool_function(filter->sinkpad, play_on_demand_get_bufferpool);
gst_pad_set_link_function(filter->sinkpad, play_on_demand_pad_connect); gst_pad_set_link_function(filter->sinkpad, play_on_demand_pad_link);
gst_element_add_pad(GST_ELEMENT(filter), filter->sinkpad); gst_element_add_pad(GST_ELEMENT(filter), filter->sinkpad);
gst_element_add_pad(GST_ELEMENT(filter), filter->srcpad); gst_element_add_pad(GST_ELEMENT(filter), filter->srcpad);
gst_element_set_loop_function(GST_ELEMENT(filter), play_on_demand_loop); gst_element_set_loop_function(GST_ELEMENT(filter), play_on_demand_loop);
filter->buffer = g_new(gchar, GST_POD_BUFFER_SIZE); filter->clock = NULL;
filter->buffer_size = GST_POD_BUFFER_SIZE;
filter->start = 0;
filter->write = 0;
/* filter properties */
filter->mute = FALSE;
filter->buffer_time = GST_POD_BUFFER_TIME;
filter->max_plays = GST_POD_MAX_PLAYS;
filter->clock_speed = GST_POD_CLOCK_SPEED;
filter->total_ticks = 1;
filter->tick_list = NULL;
/* internal buffer stuff */
play_on_demand_resize_buffer(filter);
filter->eos = FALSE; filter->eos = FALSE;
filter->buffer_filled_once = FALSE;
filter->play_from_beginning = TRUE;
filter->silent = TRUE;
filter->clock = NULL; /* play pointers, stored as an array of buffer offsets */
filter->last_time = 0; filter->write = 0;
filter->plays = g_new(guint, filter->max_plays);
filter->num_beats = GST_POD_NUM_BEATS; for (i = 0; i < filter->max_plays; i++)
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 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; filter->plays[i] = G_MAXUINT;
}
static void
play_on_demand_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
GstPlayOnDemand *filter;
register guint i;
guint new_size, min_size, *new_plays;
g_return_if_fail(GST_IS_PLAYONDEMAND(object));
filter = GST_PLAYONDEMAND(object);
switch (prop_id) {
case PROP_MUTE:
filter->mute = g_value_get_boolean(value);
break;
case PROP_BUFFER_TIME:
filter->buffer_time = g_value_get_float(value);
play_on_demand_resize_buffer(filter);
/* clear out now-invalid play pointers, if there are any. */
for (i = 0; i < filter->max_plays; i++)
if (filter->plays[i] > filter->buffer_bytes)
filter->plays[i] = G_MAXUINT;
break;
case PROP_MAX_PLAYS:
new_size = g_value_get_uint(value);
min_size = (new_size < filter->max_plays) ? new_size : filter->max_plays;
new_plays = g_new(guint, new_size);
for (i = 0; i < min_size; i++) new_plays[i] = filter->plays[i];
for (i = min_size; i < filter->max_plays; i++) new_plays[i] = G_MAXUINT;
g_free(filter->plays);
filter->plays = new_plays;
filter->max_plays = new_size;
break;
case PROP_CLOCK_SPEED:
filter->clock_speed = g_value_get_float(value);
break;
case PROP_TOTAL_TICKS:
filter->total_ticks = g_value_get_uint(value);
break;
case PROP_TICK_LIST:
filter->tick_list = (GSList *) g_value_get_pointer(value);
break;
default:
break;
} }
} }
static void
play_on_demand_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
GstPlayOnDemand *filter;
g_return_if_fail(GST_IS_PLAYONDEMAND(object));
filter = GST_PLAYONDEMAND(object);
switch (prop_id) {
case PROP_MUTE:
g_value_set_boolean(value, filter->mute);
break;
case PROP_BUFFER_TIME:
g_value_set_float(value, filter->buffer_time);
break;
case PROP_MAX_PLAYS:
g_value_set_uint(value, filter->max_plays);
break;
case PROP_CLOCK_SPEED:
g_value_set_float(value, filter->clock_speed);
break;
case PROP_TOTAL_TICKS:
g_value_set_uint(value, filter->total_ticks);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
}
static GstBufferPool*
play_on_demand_get_bufferpool (GstPad *pad)
{
GstPlayOnDemand *filter;
filter = GST_PLAYONDEMAND(gst_pad_get_parent(pad));
return gst_pad_get_bufferpool(filter->srcpad);
}
static GstPadLinkReturn
play_on_demand_pad_link (GstPad *pad, GstCaps *caps)
{
const gchar *format;
GstPlayOnDemand *filter;
g_return_val_if_fail(caps != NULL, GST_PAD_LINK_DELAYED);
g_return_val_if_fail(pad != NULL, GST_PAD_LINK_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);
} else if (strcmp(format, "float") == 0) {
filter->format = GST_PLAYONDEMAND_FORMAT_FLOAT;
}
play_on_demand_resize_buffer(filter);
if (GST_CAPS_IS_FIXED (caps))
return gst_pad_try_set_caps (filter->srcpad, caps);
return GST_PAD_LINK_DELAYED;
}
/* clock check macros. in these macros,
f == filter
t == tick in question
c == current tick (from the clock)
l == last tick (last tick when clock was checked)
dt == ticks between c and t */
#define GST_POD_SAMPLE_OFFSET(f, dt) \
((guint) (dt * (f)->rate / (f)->clock_speed))
/* play a sample if the tick in question came between the last time we checked
the clock and the current clock time. only looks complicated because the last
clock time could have been at the end of the total_ticks, so the clock might
have wrapped around ... */
#define GST_POD_TICK_ELAPSED(t, c, l) \
(((l < c) && (l < t) && (t < c)) || ((l > c) && ((l < t) || (t < c))))
static void static void
play_on_demand_loop (GstElement *elem) play_on_demand_loop (GstElement *elem)
{ {
GstPlayOnDemand *filter = GST_PLAYONDEMAND(elem); GstPlayOnDemand *filter = GST_PLAYONDEMAND(elem);
guint num_in, num_out, num_filter, max_filter; guint num_in, num_out, num_filter;
GstBuffer *in, *out; GstBuffer *in, *out;
register guint j, k, t; register guint j, k, t;
guint w, offset; guint w, offset;
/* variables for clock check. */
static guint last_tick = 0;
GSList *tick_list;
guint tick, current_tick;
g_return_if_fail(filter != NULL); g_return_if_fail(filter != NULL);
g_return_if_fail(GST_IS_PLAYONDEMAND(filter)); g_return_if_fail(GST_IS_PLAYONDEMAND(filter));
@ -369,9 +469,10 @@ static void
play_on_demand_set_clock (GstElement *elem, GstClock *clock) play_on_demand_set_clock (GstElement *elem, GstClock *clock)
{ {
GstPlayOnDemand *filter; GstPlayOnDemand *filter;
g_return_if_fail(elem != NULL);
g_return_if_fail(GST_IS_PLAYONDEMAND(elem)); g_return_if_fail(GST_IS_PLAYONDEMAND(elem));
filter = GST_PLAYONDEMAND(elem); filter = GST_PLAYONDEMAND(elem);
g_return_if_fail(filter != NULL);
filter->clock = clock; filter->clock = clock;
} }
@ -381,233 +482,86 @@ play_on_demand_play_handler (GstElement *elem)
{ {
GstPlayOnDemand *filter; GstPlayOnDemand *filter;
g_return_if_fail(elem != NULL);
g_return_if_fail(GST_IS_PLAYONDEMAND(elem)); g_return_if_fail(GST_IS_PLAYONDEMAND(elem));
filter = GST_PLAYONDEMAND(elem); filter = GST_PLAYONDEMAND(elem);
g_return_if_fail(filter != NULL);
play_on_demand_add_play_ptr(filter, filter->start); play_on_demand_add_play_pointer(filter, 0);
} }
static void static void
play_on_demand_add_play_ptr (GstPlayOnDemand *filter, guint pos) play_on_demand_clear_handler (GstElement *elem)
{ {
register guint i; GstPlayOnDemand *filter;
for (i = 0; i < GST_POD_MAX_PLAY_PTRS; i++) { g_return_if_fail(elem != NULL);
if (filter->plays[i] == G_MAXUINT) { g_return_if_fail(GST_IS_PLAYONDEMAND(elem));
filter->plays[i] = pos; filter = GST_PLAYONDEMAND(elem);
return;
} filter->write = 0;
} filter->eos = FALSE;
} }
static void static void
play_on_demand_reset_handler(GstElement *elem) play_on_demand_reset_handler (GstElement *elem)
{ {
GstPlayOnDemand *filter; GstPlayOnDemand *filter;
register guint i; register guint i;
g_return_if_fail(elem != NULL);
g_return_if_fail(GST_IS_PLAYONDEMAND(elem)); g_return_if_fail(GST_IS_PLAYONDEMAND(elem));
filter = GST_PLAYONDEMAND(elem); filter = GST_PLAYONDEMAND(elem);
g_return_if_fail(filter != NULL);
for (i = 0; i < GST_POD_MAX_PLAY_PTRS; i++) { for (i = 0; i < filter->max_plays; i++) {
filter->plays[i] = G_MAXUINT; filter->plays[i] = G_MAXUINT;
} }
filter->start = 0;
filter->write = 0; filter->write = 0;
filter->eos = FALSE; 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_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 static void
play_on_demand_set_property (GObject *object, guint prop_id, play_on_demand_add_play_pointer (GstPlayOnDemand *filter, guint pos)
const GValue *value, GParamSpec *pspec) {
register guint i;
if (filter->rate && ((filter->buffer_time * filter->rate) > pos))
for (i = 0; i < filter->max_plays; i++)
if (filter->plays[i] == G_MAXUINT) {
filter->plays[i] = pos;
/* emit a signal to indicate a sample being played */
g_signal_emit(filter, gst_pod_filter_signals[PLAYED_SIGNAL], 0);
return;
}
}
static void
play_on_demand_resize_buffer (GstPlayOnDemand *filter)
{ {
GstPlayOnDemand *filter;
register guchar c;
register guint i; register guint i;
guint new_size, min_size;
gchar *new_buffer; gchar *new_buffer;
guint64 *new_measures;
/* it's not null if we got it, but it might not be ours */ /* use a default sample rate of 44100, 1 channel, 1 byte per sample if caps
g_return_if_fail(GST_IS_PLAYONDEMAND(object)); haven't been set yet */
filter = GST_PLAYONDEMAND(object); new_size = (guint) filter->buffer_time;
g_return_if_fail(filter != NULL); new_size *= (filter->rate) ? filter->rate : 44100;
new_size *= (filter->channels) ? filter->channels : 1;
switch (prop_id) { if (filter->format && filter->format == GST_PLAYONDEMAND_FORMAT_FLOAT)
case PROP_BUFFERSIZE: new_size *= sizeof(gfloat);
filter->buffer_size = g_value_get_uint(value); else
new_size *= (filter->width) ? filter->width / 8 : 1;
if (filter->format == GST_PLAYONDEMAND_FORMAT_FLOAT) { min_size = (new_size < filter->buffer_bytes) ? new_size : filter->buffer_bytes;
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 new_buffer = g_new(gchar, new_size);
pointers. */ for (i = 0; i < min_size; i++) new_buffer[i] = filter->buffer[i];
new_buffer = g_new(gchar, filter->buffer_size); for (i = min_size; i < filter->buffer_bytes; i++) new_buffer[i] = 0;
for (c = 0; c < filter->buffer_size; c++) {
new_buffer[c] = filter->buffer[c];
}
g_free(filter->buffer); g_free(filter->buffer);
filter->buffer = new_buffer; filter->buffer = new_buffer;
filter->buffer_bytes = new_size;
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 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 PROP_PLAYFROMBEGINNING:
filter->play_from_beginning = g_value_get_boolean(value);
play_on_demand_reset_handler(GST_ELEMENT(filter));
break;
default:
break;
}
}
static void
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 PROP_BUFFERSIZE:
g_value_set_uint(value, filter->buffer_size);
break;
case PROP_SILENT:
g_value_set_boolean(value, filter->silent);
break;
case PROP_PLAYFROMBEGINNING:
g_value_set_boolean(value, filter->play_from_beginning);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
break;
}
} }
static gboolean static gboolean
@ -616,8 +570,8 @@ plugin_init (GModule *module, GstPlugin *plugin)
GstElementFactory *factory; GstElementFactory *factory;
factory = gst_element_factory_new("playondemand", factory = gst_element_factory_new("playondemand",
GST_TYPE_PLAYONDEMAND, GST_TYPE_PLAYONDEMAND,
&play_on_demand_details); &play_on_demand_details);
g_return_val_if_fail(factory != NULL, FALSE); 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_src_factory());

View file

@ -55,80 +55,45 @@ enum _GstPlayOnDemandFormat {
struct _GstPlayOnDemand { struct _GstPlayOnDemand {
GstElement element; GstElement element;
GstPad *sinkpad, *srcpad;
GstBufferPool *bufpool; GstBufferPool *bufpool;
GstPad *sinkpad, *srcpad;
GstClock *clock;
/* these next data elements are for the filter's internal buffers and list of /* filter properties */
play pointers (offsets in the internal buffers). there are also flags for gboolean mute;
repeating from the beginning or end of the input stream, and a max buffer gfloat buffer_time;
size. */ guint max_plays;
gfloat clock_speed;
guint total_ticks;
GSList *tick_list;
/* internal buffer info */
gchar *buffer; gchar *buffer;
guint buffer_size; guint buffer_bytes;
guint buffer_samples; gboolean eos;
/* play pointers == internal buffer offsets for producing output sound */
guint *plays; guint *plays;
guint write; guint write;
guint start;
gboolean play_from_beginning; /* audio format info (used to calculate buffer_samples) */
gboolean buffer_filled_once;
gboolean eos;
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; GstPlayOnDemandFormat format;
guint rate; guint rate;
guint channels; guint channels;
/* the next five are valid only for format == GST_PLAYONDEMAND_FORMAT_INT */
guint width; 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;
gfloat intercept;
}; };
struct _GstPlayOnDemandClass { struct _GstPlayOnDemandClass {
GstElementClass parent_class; GstElementClass parent_class;
void (*play) (GstElement *elem); void (*play) (GstElement *elem);
void (*reset) (GstElement *elem); void (*clear) (GstElement *elem);
void (*reset) (GstElement *elem);
void (*played) (GstElement *elem);
}; };
GType gst_play_on_demand_get_type(void); 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 #ifdef __cplusplus
} }