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 ?
This commit is contained in:
Leif Johnson 2002-10-17 20:05:58 +00:00
parent 4754e4e8cc
commit ce94093ed4
6 changed files with 460 additions and 162 deletions

2
common

@ -1 +1 @@
Subproject commit 2f0e1ecbfe7d27cf1b2215204958c95516db173d
Subproject commit cd050468e0d9e0d1b9134eb3081e2ab3228f0c01

View file

@ -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

View file

@ -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_bin_add_many(GST_BIN(pipeline), src, mad, pod, osssink, NULL);
gst_element_connect_many(src, mad, pod, osssink, NULL);
gst_element_connect(src, mad);
gst_element_connect(pod, osssink);
gst_element_connect(mad, pod);
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;
}

View file

@ -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);

View file

@ -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_play_handler (GstElement *elem);
static void play_on_demand_reset_handler (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_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_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)
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. */
g_free(filter->buffer);
filter->buffer = g_new(gchar, filter->buffer_size);
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;
}
/* reset the play pointers and read/write indexes. */
play_on_demand_reset_handler(GST_ELEMENT(filter));
/* 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 = new_buffer;
for (i = 0; i < GST_POD_MAX_PLAY_PTRS; i++) {
if (filter->plays[i] > filter->buffer_size) {
filter->plays[i] = G_MAXUINT;
}
}
break;
case ARG_SILENT:
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 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());

View file

@ -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 */