#define DEFAULT_TICK_PERIOD_MS 10
#define DEFAULT_POLL_TIMEOUT_MS (DEFAULT_TICK_PERIOD_MS / 2)
-GST_DEBUG_CATEGORY (mysrc_debug);
-#define GST_CAT_DEFAULT mysrc_debug
+GST_DEBUG_CATEGORY (mysource_debug);
+#define GST_CAT_DEFAULT mysource_debug
typedef struct _App App;
snd_seq_t *seq;
int port_count;
- snd_seq_addr_t *ports;
+ snd_seq_addr_t *seq_ports;
snd_midi_event_t *parser;
unsigned char *buffer;
guint64 tick;
};
-App s_app;
+static App s_app;
static int
init_seq (App * app)
goto error;
}
+ /*
+ * Prevent Valgrind from reporting cached configuration as memory leaks, see:
+ * http://git.alsa-project.org/?p=alsa-lib.git;a=blob;f=MEMORY-LEAK;hb=HEAD
+ */
+ snd_config_update_free_global();
+
ret = snd_seq_set_client_name (app->seq, DEFAULT_CLIENT_NAME);
if (ret < 0) {
GST_ERROR ("Cannot set client name - %s", snd_strerror (ret));
return ret;
}
-/* parses one or more port addresses from the string */
+/* Parses one or more port addresses from the string */
static int
parse_ports (const char *arg, App * app)
{
- char *buf, *s, *port_name;
- int ret;
+ gchar **ports_list;
+ guint i;
+ int ret = 0;
GST_DEBUG ("ports: %s", arg);
- /* make a copy of the string because we're going to modify it */
- buf = strdup (arg);
- if (!buf) {
+ /*
+ * Assume that ports are separated by commas.
+ *
+ * Commas are used instead of spaces because those are valid in client
+ * names.
+ */
+ ports_list = g_strsplit (arg, ",", 0);
+
+ app->port_count = g_strv_length (ports_list);
+ app->seq_ports = g_try_new (snd_seq_addr_t, app->port_count);
+ if (!app->seq_ports) {
GST_ERROR ("Out of memory");
ret = -ENOMEM;
- goto out;
+ goto out_free_ports_list;
}
- for (port_name = s = buf; s; port_name = s + 1) {
- /* Assume that ports are separated by commas. We don't use
- * spaces because those are valid in client names. */
- s = strchr (port_name, ',');
- if (s)
- *s = '\0';
-
- app->port_count++;
- app->ports =
- realloc (app->ports, app->port_count * sizeof (snd_seq_addr_t));
- if (!app->ports) {
- GST_ERROR ("Out of memory");
- ret = -ENOMEM;
- goto out_free_buf;
- }
+ for (i = 0; i < (guint)app->port_count; i++) {
+ gchar *port_name = ports_list[i];
- ret =
- snd_seq_parse_address (app->seq, &app->ports[app->port_count - 1],
+ ret = snd_seq_parse_address (app->seq, &app->seq_ports[i],
port_name);
if (ret < 0) {
- GST_ERROR ("Invalid port %s - %s", port_name, snd_strerror (ret));
- goto error_free_ports;
+ GST_ERROR ("Invalid port %s - %s", port_name,
+ snd_strerror (ret));
+ goto error_free_seq_ports;
}
}
- goto out_free_buf;
+ goto out_free_ports_list;
-error_free_ports:
- free (app->ports);
-out_free_buf:
- free (buf);
-out:
+error_free_seq_ports:
+ g_free (app->seq_ports);
+out_free_ports_list:
+ g_strfreev (ports_list);
return ret;
}
for (i = 0; i < app->port_count; ++i) {
ret =
- snd_seq_connect_from (app->seq, 0, app->ports[i].client,
- app->ports[i].port);
+ snd_seq_connect_from (app->seq, 0, app->seq_ports[i].client,
+ app->seq_ports[i].port);
if (ret < 0)
/* warning */
GST_WARNING ("Cannot connect from port %d:%d - %s",
- app->ports[i].client, app->ports[i].port, snd_strerror (ret));
+ app->seq_ports[i].client, app->seq_ports[i].port, snd_strerror (ret));
}
}
static void
push_buffer (App * app, gpointer data, guint size)
{
- GstBuffer *buffer;
GstClockTime time;
- int ret;
gpointer local_data;
+ GstBuffer *buffer;
+ int ret;
- /* read the next chunk */
buffer = gst_buffer_new ();
time = app->tick * DEFAULT_TICK_PERIOD_MS * GST_MSECOND;
GST_BUFFER_DTS (buffer) = time;
GST_BUFFER_PTS (buffer) = time;
- GST_BUFFER_OFFSET (buffer) = time;
- GST_BUFFER_DURATION (buffer) = DEFAULT_TICK_PERIOD_MS * GST_MSECOND;
- local_data = g_malloc (size);
- memcpy (local_data, data, size);
+ local_data = g_memdup (data, size);
gst_buffer_append_memory (buffer,
gst_memory_new_wrapped (0, local_data, size, 0, size, local_data,
g_free));
+ GST_MEMDUMP ("MIDI data:", local_data, size);
+
GST_DEBUG ("feed buffer %p, tick %" G_GUINT64_FORMAT " size: %u",
(gpointer) buffer, app->tick, size);
g_signal_emit_by_name (app->appsrc, "push-buffer", buffer, &ret);
ret = poll (app->pfds, app->npfds, DEFAULT_POLL_TIMEOUT_MS);
if (ret < 0) {
GST_ERROR ("ERROR in poll: %s", strerror (errno));
+ gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
} else if (ret == 0) {
push_tick_buffer (app);
} else {
do {
snd_seq_event_t *event;
err = snd_seq_event_input (app->seq, &event);
- if (err < 0)
+ if (err < 0 && err != -EAGAIN) {
+ GST_ERROR ("Error in snd_seq_event_input: %s", snd_strerror (err));
+ gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
break;
+ }
if (event) {
size_ev =
snd_midi_event_decode (app->parser, app->buffer, DEFAULT_BUFSIZE,
} else {
GST_ERROR ("Error decoding event from ALSA to output: %s",
strerror (-size_ev));
+ gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
break;
}
} else {
}
app->npfds = snd_seq_poll_descriptors_count (app->seq, POLLIN);
- app->pfds = alloca (sizeof (*app->pfds) * app->npfds);
+ app->pfds = malloc (sizeof (*app->pfds) * app->npfds);
if (app->pfds == NULL) {
ret = -ENOMEM;
goto err_free_buffer;
err_free_parser:
snd_midi_event_free (app->parser);
err_free_ports:
- free (app->ports);
+ g_free (app->seq_ports);
err_seq_close:
snd_seq_close (app->seq);
err:
app_finalize (App * app)
{
/* free the resources */
+ free (app->pfds);
free (app->buffer);
- free (app->ports);
snd_midi_event_free (app->parser);
+ g_free (app->seq_ports);
snd_seq_close (app->seq);
}
GOptionContext *ctx;
GError *err = NULL;
gchar *ports = NULL;
+ gboolean verbose = FALSE;
GOptionEntry options[] = {
{"ports", 'p', 0, G_OPTION_ARG_STRING, &ports,
"Comma separated list of sequencer ports", "client:port,..."},
+ {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+ "Output status information and property notifications", NULL},
{NULL}
};
gst_init (&argc, &argv);
- GST_DEBUG_CATEGORY_INIT (mysrc_debug, "mysrc", 0,
+ GST_DEBUG_CATEGORY_INIT (mysource_debug, "mysource", 0,
"ALSA MIDI sequencer appsrc pipeline");
ret = app_init (app, ports);
("appsrc name=mysource ! fluiddec ! audioconvert ! autoaudiosink", NULL);
g_assert (app->pipeline);
+ if (verbose)
+ g_signal_connect (app->pipeline, "deep-notify", G_CALLBACK (gst_object_default_deep_notify), NULL);
+
bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
g_assert (bus);