mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
+ 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:
parent
b2d148685a
commit
6b785348c4
5 changed files with 471 additions and 531 deletions
1
gst/playondemand/.gitignore
vendored
1
gst/playondemand/.gitignore
vendored
|
@ -1,2 +1 @@
|
|||
demo-mp3
|
||||
demo_mp3
|
||||
|
|
|
@ -3,14 +3,25 @@
|
|||
#include <gtk/gtk.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "gstplayondemand.h"
|
||||
#define NUM_BEATS 16
|
||||
#define SPEED 1e-9
|
||||
|
||||
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;
|
||||
GtkWidget *window, *vbox, *beat_box, *button_box;
|
||||
GtkWidget *play_button, *clear_button, *reset_button, *quit_button;
|
||||
GtkWidget **beat_button;
|
||||
GtkWidget *speed_scale;
|
||||
GtkObject *speed_adj;
|
||||
GstElement *src, *mad, *pod, *osssink, *pipeline;
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
clear (GtkButton *button, gpointer data)
|
||||
{
|
||||
g_signal_emit_by_name(G_OBJECT(pod), "clear", NULL, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
reset (GtkButton *button, gpointer data)
|
||||
{
|
||||
guint i;
|
||||
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
|
||||
measure (GtkToggleButton *button, gpointer data)
|
||||
beat (GtkToggleButton *button, gpointer data)
|
||||
{
|
||||
gst_play_on_demand_toggle_beat(GST_PLAYONDEMAND(pod),
|
||||
GPOINTER_TO_UINT(data), 0);
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
|
||||
beats = g_slist_append(beats, data);
|
||||
else
|
||||
beats = g_slist_remove(beats, data);
|
||||
g_object_set(G_OBJECT(pod), "tick-list", beats, NULL);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -46,9 +70,9 @@ setup_pipeline (gchar *filename)
|
|||
osssink = gst_element_factory_make("osssink", "osssink");
|
||||
|
||||
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);
|
||||
g_object_set(G_OBJECT(pod), "total-ticks", NUM_BEATS,
|
||||
"clock-speed", SPEED, NULL);
|
||||
|
||||
pipeline = gst_pipeline_new("app");
|
||||
|
||||
|
@ -57,69 +81,79 @@ setup_pipeline (gchar *filename)
|
|||
|
||||
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)
|
||||
{
|
||||
guint i;
|
||||
|
||||
beat_button = g_new(GtkWidget *, NUM_BEATS);
|
||||
|
||||
/* initialize gui elements ... */
|
||||
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(window), 12);
|
||||
|
||||
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); */
|
||||
gtk_box_set_spacing(GTK_BOX(vbox), 12);
|
||||
|
||||
beat_box = gtk_hbox_new(TRUE, 0);
|
||||
button_box = gtk_hbox_new(TRUE, 0);
|
||||
|
||||
play_button = gtk_button_new_with_label("Play");
|
||||
clear_button = gtk_button_new_with_label("Reset Sound");
|
||||
reset_button = gtk_button_new_with_label("Reset All");
|
||||
quit_button = gtk_button_new_with_label("Quit");
|
||||
|
||||
for (i = 0; i < NUM_BEATS; i++)
|
||||
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 ... */
|
||||
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, 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);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(button_box), play_button, TRUE, TRUE, 2);
|
||||
gtk_box_pack_start(GTK_BOX(button_box), clear_button, TRUE, TRUE, 2);
|
||||
gtk_box_pack_start(GTK_BOX(button_box), reset_button, TRUE, TRUE, 2);
|
||||
gtk_box_pack_start(GTK_BOX(button_box), quit_button, TRUE, TRUE, 2);
|
||||
|
||||
for (i = 0; i < NUM_BEATS; i++)
|
||||
gtk_box_pack_start(GTK_BOX(beat_box), beat_button[i], TRUE, TRUE, 2);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), button_box, 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 ... */
|
||||
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(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);*/
|
||||
g_signal_connect(G_OBJECT(pod), "played", G_CALLBACK(played), NULL);
|
||||
g_signal_connect(G_OBJECT(speed_adj), "value_changed", G_CALLBACK(speed), NULL);
|
||||
for (i = 0; i < NUM_BEATS; i++)
|
||||
g_signal_connect(G_OBJECT(beat_button[i]), "toggled", G_CALLBACK(beat), GUINT_TO_POINTER(i));
|
||||
|
||||
/* show the gui. */
|
||||
gtk_widget_show(play_button);
|
||||
gtk_widget_show(clear_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);*/
|
||||
|
||||
for (i = 0; i < NUM_BEATS; i++)
|
||||
gtk_widget_show(beat_button[i]);
|
||||
|
||||
gtk_widget_show(beat_box);
|
||||
gtk_widget_show(button_box);
|
||||
gtk_widget_show(speed_scale);
|
||||
gtk_widget_show(vbox);
|
||||
gtk_widget_show(window);
|
||||
|
||||
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);
|
||||
setup_gui();
|
||||
gtk_main();
|
||||
g_free(beat_button);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -3,18 +3,19 @@
|
|||
_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;
|
||||
num_filter = filter->buffer_bytes / sizeof(_TYPE_);
|
||||
|
||||
/******************************************************************************/
|
||||
/* see if we've got any events coming through ... */
|
||||
|
||||
do {
|
||||
GST_DEBUG(0, "--- going to events");
|
||||
|
||||
while (! filter->eos && GST_IS_EVENT(in)) {
|
||||
while (GST_IS_EVENT(in)) {
|
||||
if (GST_EVENT_TYPE(in) == GST_EVENT_EOS) {
|
||||
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 {
|
||||
gst_pad_push(filter->srcpad, in);
|
||||
}
|
||||
|
@ -25,8 +26,6 @@ do {
|
|||
/****************************************************************************/
|
||||
/* 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. */
|
||||
if (! filter->eos) {
|
||||
data_in = (_TYPE_ *) GST_BUFFER_DATA(in);
|
||||
|
@ -35,27 +34,13 @@ do {
|
|||
w = filter->write;
|
||||
|
||||
/* 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->write = (w + j) % num_filter;
|
||||
|
||||
if ((w + j) >= num_filter) {
|
||||
filter->buffer_filled_once = 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;
|
||||
}
|
||||
if ((w + j) >= num_filter)
|
||||
filter->eos = TRUE;
|
||||
|
||||
out = in;
|
||||
} else {
|
||||
|
@ -68,56 +53,58 @@ do {
|
|||
/****************************************************************************/
|
||||
/* 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);
|
||||
|
||||
/****************************************************************************/
|
||||
/* now handle output data. */
|
||||
|
||||
GST_DEBUG(0, "--- done with clock, going to output");
|
||||
|
||||
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. */
|
||||
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;
|
||||
if (current_tick != last_tick) {
|
||||
/* now we go through the tick list and play samples */
|
||||
tick_list = filter->tick_list;
|
||||
while (tick_list) {
|
||||
tick = GPOINTER_TO_UINT(tick_list->data);
|
||||
if (current_tick == tick)
|
||||
play_on_demand_add_play_pointer(filter, 0);
|
||||
else if (GST_POD_TICK_ELAPSED(tick, current_tick, last_tick))
|
||||
play_on_demand_add_play_pointer(filter, GST_POD_SAMPLE_OFFSET(filter, current_tick - tick));
|
||||
tick_list = g_slist_next(tick_list);
|
||||
}
|
||||
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);
|
||||
|
||||
if (! filter->eos) {
|
||||
if (! filter->eos)
|
||||
in = gst_pad_pull(filter->sinkpad);
|
||||
}
|
||||
|
||||
gst_element_yield (GST_ELEMENT (filter));
|
||||
gst_element_interrupt (GST_ELEMENT (filter));
|
||||
|
||||
} while (TRUE);
|
||||
|
|
|
@ -17,24 +17,25 @@
|
|||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
|
||||
#include "gstplayondemand.h"
|
||||
|
||||
|
||||
#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 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 ... */
|
||||
/* some default values */
|
||||
#define GST_POD_MAX_PLAYS 100 /* maximum simultaneous plays */
|
||||
#define GST_POD_BUFFER_TIME 5.0 /* buffer length in seconds */
|
||||
#define GST_POD_CLOCK_SPEED 1e-8 /* 0.1 sec/tick default */
|
||||
|
||||
/* 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 = {
|
||||
"Play On Demand",
|
||||
"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",
|
||||
VERSION,
|
||||
"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*
|
||||
play_on_demand_sink_factory (void)
|
||||
{
|
||||
|
@ -73,15 +55,16 @@ play_on_demand_sink_factory (void)
|
|||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
static GstPadTemplate*
|
||||
play_on_demand_src_factory (void)
|
||||
{
|
||||
|
@ -99,97 +82,27 @@ play_on_demand_src_factory (void)
|
|||
return template;
|
||||
}
|
||||
|
||||
|
||||
/* GObject functionality */
|
||||
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 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);
|
||||
/* GStreamer functionality */
|
||||
static GstBufferPool* play_on_demand_get_bufferpool (GstPad *pad);
|
||||
static GstPadLinkReturn play_on_demand_pad_link (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_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_update_plays_from_clock (GstPlayOnDemand *filter);
|
||||
|
||||
static GstElementClass *parent_class = NULL;
|
||||
|
||||
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;
|
||||
}
|
||||
/* utility functions */
|
||||
static void play_on_demand_add_play_pointer (GstPlayOnDemand *filter, guint pos);
|
||||
static void play_on_demand_resize_buffer (GstPlayOnDemand *filter);
|
||||
|
||||
GType
|
||||
gst_play_on_demand_get_type (void)
|
||||
|
@ -215,6 +128,31 @@ gst_play_on_demand_get_type (void)
|
|||
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
|
||||
play_on_demand_class_init (GstPlayOnDemandClass *klass)
|
||||
{
|
||||
|
@ -224,6 +162,15 @@ play_on_demand_class_init (GstPlayOnDemandClass *klass)
|
|||
gobject_class = (GObjectClass *) 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] =
|
||||
g_signal_new("play",
|
||||
G_TYPE_FROM_CLASS(klass),
|
||||
|
@ -233,6 +180,15 @@ play_on_demand_class_init (GstPlayOnDemandClass *klass)
|
|||
g_cclosure_marshal_VOID__VOID,
|
||||
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] =
|
||||
g_signal_new("reset",
|
||||
G_TYPE_FROM_CLASS(klass),
|
||||
|
@ -243,34 +199,45 @@ play_on_demand_class_init (GstPlayOnDemandClass *klass)
|
|||
G_TYPE_NONE, 0);
|
||||
|
||||
klass->play = play_on_demand_play_handler;
|
||||
klass->clear = play_on_demand_clear_handler;
|
||||
klass->reset = play_on_demand_reset_handler;
|
||||
|
||||
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->get_property = play_on_demand_get_property;
|
||||
|
||||
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
|
||||
|
@ -282,51 +249,184 @@ play_on_demand_init (GstPlayOnDemand *filter)
|
|||
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_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->srcpad);
|
||||
|
||||
gst_element_set_loop_function(GST_ELEMENT(filter), play_on_demand_loop);
|
||||
|
||||
filter->buffer = g_new(gchar, GST_POD_BUFFER_SIZE);
|
||||
filter->buffer_size = GST_POD_BUFFER_SIZE;
|
||||
filter->start = 0;
|
||||
filter->write = 0;
|
||||
filter->clock = NULL;
|
||||
|
||||
/* 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->buffer_filled_once = FALSE;
|
||||
filter->play_from_beginning = TRUE;
|
||||
filter->silent = TRUE;
|
||||
|
||||
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 the `invalid' index). */
|
||||
filter->plays = g_new(guint, GST_POD_MAX_PLAY_PTRS);
|
||||
for (i = 0; i < GST_POD_MAX_PLAY_PTRS; i++) {
|
||||
/* play pointers, stored as an array of buffer offsets */
|
||||
filter->write = 0;
|
||||
filter->plays = g_new(guint, filter->max_plays);
|
||||
for (i = 0; i < filter->max_plays; i++)
|
||||
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
|
||||
play_on_demand_loop (GstElement *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;
|
||||
register guint j, k, t;
|
||||
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(GST_IS_PLAYONDEMAND(filter));
|
||||
|
||||
|
@ -369,9 +469,10 @@ static void
|
|||
play_on_demand_set_clock (GstElement *elem, GstClock *clock)
|
||||
{
|
||||
GstPlayOnDemand *filter;
|
||||
|
||||
g_return_if_fail(elem != NULL);
|
||||
g_return_if_fail(GST_IS_PLAYONDEMAND(elem));
|
||||
filter = GST_PLAYONDEMAND(elem);
|
||||
g_return_if_fail(filter != NULL);
|
||||
|
||||
filter->clock = clock;
|
||||
}
|
||||
|
@ -381,233 +482,86 @@ play_on_demand_play_handler (GstElement *elem)
|
|||
{
|
||||
GstPlayOnDemand *filter;
|
||||
|
||||
g_return_if_fail(elem != NULL);
|
||||
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);
|
||||
play_on_demand_add_play_pointer(filter, 0);
|
||||
}
|
||||
|
||||
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++) {
|
||||
if (filter->plays[i] == G_MAXUINT) {
|
||||
filter->plays[i] = pos;
|
||||
return;
|
||||
}
|
||||
}
|
||||
g_return_if_fail(elem != NULL);
|
||||
g_return_if_fail(GST_IS_PLAYONDEMAND(elem));
|
||||
filter = GST_PLAYONDEMAND(elem);
|
||||
|
||||
filter->write = 0;
|
||||
filter->eos = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
play_on_demand_reset_handler(GstElement *elem)
|
||||
play_on_demand_reset_handler (GstElement *elem)
|
||||
{
|
||||
GstPlayOnDemand *filter;
|
||||
register guint i;
|
||||
|
||||
g_return_if_fail(elem != NULL);
|
||||
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++) {
|
||||
for (i = 0; i < filter->max_plays; 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_add_play_pointer (GstPlayOnDemand *filter, guint pos)
|
||||
{
|
||||
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;
|
||||
guint new_size, min_size;
|
||||
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);
|
||||
/* use a default sample rate of 44100, 1 channel, 1 byte per sample if caps
|
||||
haven't been set yet */
|
||||
new_size = (guint) filter->buffer_time;
|
||||
new_size *= (filter->rate) ? filter->rate : 44100;
|
||||
new_size *= (filter->channels) ? filter->channels : 1;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_BUFFERSIZE:
|
||||
filter->buffer_size = g_value_get_uint(value);
|
||||
if (filter->format && filter->format == GST_PLAYONDEMAND_FORMAT_FLOAT)
|
||||
new_size *= sizeof(gfloat);
|
||||
else
|
||||
new_size *= (filter->width) ? filter->width / 8 : 1;
|
||||
|
||||
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;
|
||||
}
|
||||
min_size = (new_size < filter->buffer_bytes) ? new_size : filter->buffer_bytes;
|
||||
|
||||
/* 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];
|
||||
}
|
||||
new_buffer = g_new(gchar, new_size);
|
||||
for (i = 0; i < min_size; i++) new_buffer[i] = filter->buffer[i];
|
||||
for (i = min_size; i < filter->buffer_bytes; i++) new_buffer[i] = 0;
|
||||
|
||||
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 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;
|
||||
}
|
||||
g_free(filter->buffer);
|
||||
filter->buffer = new_buffer;
|
||||
filter->buffer_bytes = new_size;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -616,8 +570,8 @@ plugin_init (GModule *module, GstPlugin *plugin)
|
|||
GstElementFactory *factory;
|
||||
|
||||
factory = gst_element_factory_new("playondemand",
|
||||
GST_TYPE_PLAYONDEMAND,
|
||||
&play_on_demand_details);
|
||||
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());
|
||||
|
|
|
@ -55,80 +55,45 @@ enum _GstPlayOnDemandFormat {
|
|||
struct _GstPlayOnDemand {
|
||||
GstElement element;
|
||||
|
||||
GstPad *sinkpad, *srcpad;
|
||||
GstBufferPool *bufpool;
|
||||
GstPad *sinkpad, *srcpad;
|
||||
GstClock *clock;
|
||||
|
||||
/* these next data elements are for the filter's internal buffers and list of
|
||||
play pointers (offsets in the internal buffers). there are also flags for
|
||||
repeating from the beginning or end of the input stream, and a max buffer
|
||||
size. */
|
||||
/* filter properties */
|
||||
gboolean mute;
|
||||
gfloat buffer_time;
|
||||
guint max_plays;
|
||||
gfloat clock_speed;
|
||||
guint total_ticks;
|
||||
GSList *tick_list;
|
||||
|
||||
/* internal buffer info */
|
||||
gchar *buffer;
|
||||
guint buffer_size;
|
||||
guint buffer_samples;
|
||||
guint buffer_bytes;
|
||||
gboolean eos;
|
||||
|
||||
/* play pointers == internal buffer offsets for producing output sound */
|
||||
guint *plays;
|
||||
guint write;
|
||||
guint start;
|
||||
|
||||
gboolean play_from_beginning;
|
||||
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 */
|
||||
/* audio format info (used to calculate buffer_samples) */
|
||||
GstPlayOnDemandFormat format;
|
||||
guint rate;
|
||||
guint channels;
|
||||
|
||||
/* the next five are valid only for format == GST_PLAYONDEMAND_FORMAT_INT */
|
||||
guint rate;
|
||||
guint channels;
|
||||
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 {
|
||||
GstElementClass parent_class;
|
||||
|
||||
void (*play) (GstElement *elem);
|
||||
void (*reset) (GstElement *elem);
|
||||
void (*play) (GstElement *elem);
|
||||
void (*clear) (GstElement *elem);
|
||||
void (*reset) (GstElement *elem);
|
||||
void (*played) (GstElement *elem);
|
||||
};
|
||||
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue