From bd7009756cfbcd1e52f10649f24705ed78a44778 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 19 Sep 2016 09:16:58 +0200 Subject: [PATCH] alsa: aad alsa source and monitor --- pinos/Makefile.am | 2 +- pinos/modules/spa/module.c | 4 +- pinos/modules/spa/spa-alsa-monitor.c | 389 +++++++++++++++++++++++++++ pinos/modules/spa/spa-alsa-monitor.h | 58 ++++ pinos/modules/spa/spa-alsa-sink.c | 298 -------------------- pinos/modules/spa/spa-alsa-sink.h | 61 ----- pinos/server/daemon.c | 52 ++-- spa/lib/audio-raw.c | 2 +- spa/plugins/alsa/alsa-sink.c | 124 +++------ spa/plugins/alsa/alsa-utils.c | 269 +++++++++++------- spa/plugins/alsa/alsa.c | 8 + spa/plugins/alsa/meson.build | 7 +- 12 files changed, 707 insertions(+), 567 deletions(-) create mode 100644 pinos/modules/spa/spa-alsa-monitor.c create mode 100644 pinos/modules/spa/spa-alsa-monitor.h delete mode 100644 pinos/modules/spa/spa-alsa-sink.c delete mode 100644 pinos/modules/spa/spa-alsa-sink.h diff --git a/pinos/Makefile.am b/pinos/Makefile.am index 359fb7402..d711b5003 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -246,7 +246,7 @@ module_LTLIBRARIES += module-spa.la module_spa_la_SOURCES = \ modules/spa/spa-audiotestsrc.c \ - modules/spa/spa-alsa-sink.c \ + modules/spa/spa-alsa-monitor.c \ modules/spa/spa-v4l2-monitor.c \ modules/spa/module.c module_spa_la_CFLAGS = $(AM_CFLAGS) diff --git a/pinos/modules/spa/module.c b/pinos/modules/spa/module.c index 7722262e4..20474485a 100644 --- a/pinos/modules/spa/module.c +++ b/pinos/modules/spa/module.c @@ -21,7 +21,7 @@ #include #include -#include "spa-alsa-sink.h" +#include "spa-alsa-monitor.h" #include "spa-v4l2-monitor.h" #include "spa-audiotestsrc.h" @@ -30,7 +30,7 @@ gboolean pinos__module_init (PinosModule *module, const gchar * args); G_MODULE_EXPORT gboolean pinos__module_init (PinosModule * module, G_GNUC_UNUSED const gchar * args) { - pinos_spa_alsa_sink_new (module->daemon, "alsa-sink", NULL); + pinos_spa_alsa_monitor_new (module->daemon); pinos_spa_v4l2_monitor_new (module->daemon); pinos_spa_audiotestsrc_new (module->daemon, "audiotestsrc", NULL); diff --git a/pinos/modules/spa/spa-alsa-monitor.c b/pinos/modules/spa/spa-alsa-monitor.c new file mode 100644 index 000000000..7a342e057 --- /dev/null +++ b/pinos/modules/spa/spa-alsa-monitor.c @@ -0,0 +1,389 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "spa-alsa-monitor.h" + +#define PINOS_SPA_ALSA_MONITOR_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SPA_ALSA_MONITOR, PinosSpaALSAMonitorPrivate)) + +struct _PinosSpaALSAMonitorPrivate +{ + PinosDaemon *daemon; + + SpaHandle *handle; + SpaMonitor *monitor; + + GSource *watch_source; + + unsigned int n_poll; + SpaPollItem poll[16]; + + GHashTable *nodes; +}; + +enum +{ + PROP_0, + PROP_DAEMON, + PROP_MONITOR, +}; + +G_DEFINE_TYPE (PinosSpaALSAMonitor, pinos_spa_alsa_monitor, G_TYPE_OBJECT); + +static SpaResult +make_handle (SpaHandle **handle, const char *lib, const char *name, const SpaDict *info) +{ + SpaResult res; + void *hnd, *state = NULL; + SpaEnumHandleFactoryFunc enum_func; + + if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) { + g_error ("can't load %s: %s", lib, dlerror()); + return SPA_RESULT_ERROR; + } + if ((enum_func = dlsym (hnd, "spa_enum_handle_factory")) == NULL) { + g_error ("can't find enum function"); + return SPA_RESULT_ERROR; + } + + while (true) { + const SpaHandleFactory *factory; + + if ((res = enum_func (&factory, &state)) < 0) { + if (res != SPA_RESULT_ENUM_END) + g_error ("can't enumerate factories: %d", res); + break; + } + if (strcmp (factory->name, name)) + continue; + + *handle = g_malloc0 (factory->size); + if ((res = spa_handle_factory_init (factory, *handle, info)) < 0) { + g_error ("can't make factory instance: %d", res); + return res; + } + return SPA_RESULT_OK; + } + return SPA_RESULT_ERROR; +} + +static void +add_item (PinosSpaALSAMonitor *this, SpaMonitorItem *item) +{ + PinosSpaALSAMonitorPrivate *priv = this->priv; + SpaResult res; + SpaHandle *handle; + PinosNode *node; + void *iface; + PinosProperties *props = NULL; + + g_debug ("alsa-monitor %p: add: \"%s\" (%s)", this, item->name, item->id); + + handle = calloc (1, item->factory->size); + if ((res = spa_handle_factory_init (item->factory, handle, item->info)) < 0) { + g_error ("can't make factory instance: %d", res); + return; + } + if ((res = spa_handle_get_interface (handle, SPA_INTERFACE_ID_NODE, &iface)) < 0) { + g_error ("can't get MONITOR interface: %d", res); + return; + } + + if (item->info) { + unsigned int i; + + props = pinos_properties_new (NULL, NULL); + + for (i = 0; i < item->info->n_items; i++) + pinos_properties_set (props, + item->info->items[i].key, + item->info->items[i].value); + } + + node = g_object_new (PINOS_TYPE_NODE, + "daemon", priv->daemon, + "name", item->factory->name, + "node", iface, + "properties", props, + NULL); + + g_hash_table_insert (priv->nodes, g_strdup (item->id), node); +} + +static void +remove_item (PinosSpaALSAMonitor *this, SpaMonitorItem *item) +{ + PinosSpaALSAMonitorPrivate *priv = this->priv; + PinosNode *node; + + g_debug ("alsa-monitor %p: remove: \"%s\" (%s)", this, item->name, item->id); + + node = g_hash_table_lookup (priv->nodes, item->id); + if (node) { + pinos_node_remove (node); + g_hash_table_remove (priv->nodes, item->id); + } +} + +static gboolean +poll_event (GIOChannel *source, + GIOCondition condition, + gpointer user_data) +{ + PinosSpaALSAMonitor *this = user_data; + PinosSpaALSAMonitorPrivate *priv = this->priv; + SpaPollNotifyData data; + + data.user_data = priv->poll[0].user_data; + data.fds = priv->poll[0].fds; + data.n_fds = priv->poll[0].n_fds; + priv->poll[0].after_cb (&data); + + return TRUE; +} + +static void +on_monitor_event (SpaMonitor *monitor, + SpaMonitorEvent *event, + void *user_data) +{ + PinosSpaALSAMonitor *this = user_data; + PinosSpaALSAMonitorPrivate *priv = this->priv; + + switch (event->type) { + case SPA_MONITOR_EVENT_TYPE_ADDED: + { + SpaMonitorItem *item = event->data; + add_item (this, item); + break; + } + case SPA_MONITOR_EVENT_TYPE_REMOVED: + { + SpaMonitorItem *item = event->data; + remove_item (this, item); + } + case SPA_MONITOR_EVENT_TYPE_CHANGED: + { + SpaMonitorItem *item = event->data; + g_debug ("alsa-monitor %p: changed: \"%s\"", this, item->name); + break; + } + case SPA_MONITOR_EVENT_TYPE_ADD_POLL: + { + SpaPollItem *item = event->data; + GIOChannel *channel; + + priv->poll[priv->n_poll] = *item; + priv->n_poll++; + + channel = g_io_channel_unix_new (item->fds[0].fd); + priv->watch_source = g_io_create_watch (channel, G_IO_IN); + g_io_channel_unref (channel); + g_source_set_callback (priv->watch_source, (GSourceFunc) poll_event, this, NULL); + g_source_attach (priv->watch_source, g_main_context_get_thread_default ()); + g_source_unref (priv->watch_source); + break; + } + case SPA_MONITOR_EVENT_TYPE_UPDATE_POLL: + break; + case SPA_MONITOR_EVENT_TYPE_REMOVE_POLL: + { + priv->n_poll--; + g_source_destroy (priv->watch_source); + priv->watch_source = NULL; + break; + } + default: + break; + } +} + +static void +monitor_constructed (GObject * object) +{ + PinosSpaALSAMonitor *this = PINOS_SPA_ALSA_MONITOR (object); + PinosSpaALSAMonitorPrivate *priv = this->priv; + SpaResult res; + void *state = NULL; + + g_debug ("spa-monitor %p: constructed", this); + + G_OBJECT_CLASS (pinos_spa_alsa_monitor_parent_class)->constructed (object); + + while (TRUE) { + SpaMonitorItem *item; + + if ((res = spa_monitor_enum_items (priv->monitor, &item, &state)) < 0) { + if (res != SPA_RESULT_ENUM_END) + g_debug ("spa_monitor_enum_items: got error %d\n", res); + break; + } + add_item (this, item); + } + spa_monitor_set_event_callback (priv->monitor, on_monitor_event, this); +} + +static void +monitor_get_property (GObject *_object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PinosSpaALSAMonitor *this = PINOS_SPA_ALSA_MONITOR (_object); + PinosSpaALSAMonitorPrivate *priv = this->priv; + + switch (prop_id) { + case PROP_DAEMON: + g_value_set_object (value, priv->daemon); + break; + + case PROP_MONITOR: + g_value_set_pointer (value, priv->monitor); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (this, prop_id, pspec); + break; + } +} + +static void +monitor_set_property (GObject *_object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PinosSpaALSAMonitor *this = PINOS_SPA_ALSA_MONITOR (_object); + PinosSpaALSAMonitorPrivate *priv = this->priv; + + switch (prop_id) { + case PROP_DAEMON: + priv->daemon = g_value_dup_object (value); + break; + + case PROP_MONITOR: + priv->monitor = g_value_get_pointer (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (this, prop_id, pspec); + break; + } +} + +static void +monitor_finalize (GObject * object) +{ + PinosSpaALSAMonitor *this = PINOS_SPA_ALSA_MONITOR (object); + PinosSpaALSAMonitorPrivate *priv = this->priv; + + g_debug ("spa-monitor %p: dispose", this); + spa_handle_clear (priv->handle); + g_free (priv->handle); + g_hash_table_unref (priv->nodes); + + G_OBJECT_CLASS (pinos_spa_alsa_monitor_parent_class)->finalize (object); +} + +static void +pinos_spa_alsa_monitor_class_init (PinosSpaALSAMonitorClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (PinosSpaALSAMonitorPrivate)); + + gobject_class->constructed = monitor_constructed; + gobject_class->finalize = monitor_finalize; + gobject_class->set_property = monitor_set_property; + gobject_class->get_property = monitor_get_property; + + g_object_class_install_property (gobject_class, + PROP_DAEMON, + g_param_spec_object ("daemon", + "Daemon", + "The Daemon", + PINOS_TYPE_DAEMON, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_MONITOR, + g_param_spec_pointer ("monitor", + "Monitor", + "The SPA monitor", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +pinos_spa_alsa_monitor_init (PinosSpaALSAMonitor * this) +{ + PinosSpaALSAMonitorPrivate *priv = this->priv = PINOS_SPA_ALSA_MONITOR_GET_PRIVATE (this); + + priv->nodes = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_object_unref); +} + +GObject * +pinos_spa_alsa_monitor_new (PinosDaemon *daemon) +{ + GObject *monitor; + SpaHandle *handle; + SpaResult res; + void *iface; + + if ((res = make_handle (&handle, + "spa/build/plugins/alsa/libspa-alsa.so", + "alsa-monitor", + NULL)) < 0) { + g_error ("can't create alsa-monitor: %d", res); + return NULL; + } + + if ((res = spa_handle_get_interface (handle, SPA_INTERFACE_ID_MONITOR, &iface)) < 0) { + g_free (handle); + g_error ("can't get MONITOR interface: %d", res); + return NULL; + } + + monitor = g_object_new (PINOS_TYPE_SPA_ALSA_MONITOR, + "daemon", daemon, + "monitor", iface, + NULL); + return monitor; +} diff --git a/pinos/modules/spa/spa-alsa-monitor.h b/pinos/modules/spa/spa-alsa-monitor.h new file mode 100644 index 000000000..a05a6e021 --- /dev/null +++ b/pinos/modules/spa/spa-alsa-monitor.h @@ -0,0 +1,58 @@ +/* Pinos + * Copyright (C) 2015 Wim Taymans + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __PINOS_SPA_ALSA_MONITOR_H__ +#define __PINOS_SPA_ALSA_MONITOR_H__ + +#include + +#include + +G_BEGIN_DECLS + +#define PINOS_TYPE_SPA_ALSA_MONITOR (pinos_spa_alsa_monitor_get_type ()) +#define PINOS_IS_SPA_ALSA_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SPA_ALSA_MONITOR)) +#define PINOS_IS_SPA_ALSA_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SPA_ALSA_MONITOR)) +#define PINOS_SPA_ALSA_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SPA_ALSA_MONITOR, PinosSpaALSAMonitorClass)) +#define PINOS_SPA_ALSA_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SPA_ALSA_MONITOR, PinosSpaALSAMonitor)) +#define PINOS_SPA_ALSA_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SPA_ALSA_MONITOR, PinosSpaALSAMonitorClass)) +#define PINOS_SPA_ALSA_MONITOR_CAST(obj) ((PinosSpaALSAMonitor*)(obj)) +#define PINOS_SPA_ALSA_MONITOR_CLASS_CAST(klass) ((PinosSpaALSAMonitorClass*)(klass)) + +typedef struct _PinosSpaALSAMonitor PinosSpaALSAMonitor; +typedef struct _PinosSpaALSAMonitorClass PinosSpaALSAMonitorClass; +typedef struct _PinosSpaALSAMonitorPrivate PinosSpaALSAMonitorPrivate; + +struct _PinosSpaALSAMonitor { + GObject object; + + PinosSpaALSAMonitorPrivate *priv; +}; + +struct _PinosSpaALSAMonitorClass { + GObjectClass parent_class; +}; + +GType pinos_spa_alsa_monitor_get_type (void); + +GObject * pinos_spa_alsa_monitor_new (PinosDaemon *daemon); + +G_END_DECLS + +#endif /* __PINOS_SPA_ALSA_MONITOR_H__ */ diff --git a/pinos/modules/spa/spa-alsa-sink.c b/pinos/modules/spa/spa-alsa-sink.c deleted file mode 100644 index 8a7744566..000000000 --- a/pinos/modules/spa/spa-alsa-sink.c +++ /dev/null @@ -1,298 +0,0 @@ -/* Pinos - * Copyright (C) 2015 Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include "spa-alsa-sink.h" - -#define PINOS_SPA_ALSA_SINK_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkPrivate)) - -struct _PinosSpaAlsaSinkPrivate -{ - PinosRingbuffer *ringbuffer; -}; - -enum { - PROP_0, -}; - -G_DEFINE_TYPE (PinosSpaAlsaSink, pinos_spa_alsa_sink, PINOS_TYPE_NODE); - -static SpaResult -make_node (SpaNode **node, const char *lib, const char *name) -{ - SpaHandle *handle; - SpaResult res; - void *hnd, *state = NULL; - SpaEnumHandleFactoryFunc enum_func; - - if ((hnd = dlopen (lib, RTLD_NOW)) == NULL) { - g_error ("can't load %s: %s", lib, dlerror()); - return SPA_RESULT_ERROR; - } - if ((enum_func = dlsym (hnd, "spa_enum_handle_factory")) == NULL) { - g_error ("can't find enum function"); - return SPA_RESULT_ERROR; - } - - while (true) { - const SpaHandleFactory *factory; - void *iface; - - if ((res = enum_func (&factory, &state)) < 0) { - if (res != SPA_RESULT_ENUM_END) - g_error ("can't enumerate factories: %d", res); - break; - } - if (strcmp (factory->name, name)) - continue; - - handle = calloc (1, factory->size); - if ((res = spa_handle_factory_init (factory, handle, NULL)) < 0) { - g_error ("can't make factory instance: %d", res); - return res; - } - if ((res = spa_handle_get_interface (handle, SPA_INTERFACE_ID_NODE, &iface)) < 0) { - g_error ("can't get interface %d", res); - return res; - } - *node = iface; - return SPA_RESULT_OK; - } - return SPA_RESULT_ERROR; -} - -#if 0 -static void -on_sink_event (SpaNode *node, SpaNodeEvent *event, void *user_data) -{ - PinosSpaAlsaSink *this = user_data; - PinosSpaAlsaSinkPrivate *priv = this->priv; - - switch (event->type) { - case SPA_NODE_EVENT_TYPE_NEED_INPUT: - { - SpaPortInputInfo iinfo; - SpaResult res; - PinosRingbufferArea areas[2]; - uint8_t *data; - size_t size, towrite, total; - SpaNodeEventNeedInput *ni = event->data; - - size = 0; - data = NULL; - - pinos_ringbuffer_get_read_areas (priv->ringbuffer, areas); - - total = MIN (size, areas[0].len + areas[1].len); - g_debug ("total read %zd %zd %zd", total, size, areas[0].len + areas[1].len); - if (total < size) { - g_warning ("underrun"); - } - towrite = MIN (size, areas[0].len); - memcpy (data, areas[0].data, towrite); - size -= towrite; - data += towrite; - towrite = MIN (size, areas[1].len); - memcpy (data, areas[1].data, towrite); - - pinos_ringbuffer_read_advance (priv->ringbuffer, total); - - iinfo.port_id = ni->port_id; - iinfo.flags = SPA_PORT_INPUT_FLAG_NONE; - iinfo.buffer_id = 0; - - g_debug ("push sink %d", iinfo.buffer_id); - if ((res = spa_node_port_push_input (node, 1, &iinfo)) < 0) - g_debug ("got error %d", res); - break; - } - default: - g_debug ("got event %d", event->type); - break; - } -} -#endif - -static void -setup_node (PinosSpaAlsaSink *this) -{ - PinosNode *node = PINOS_NODE (this); - SpaResult res; - SpaProps *props; - SpaPropValue value; - - if ((res = spa_node_get_props (node->node, &props)) < 0) - g_debug ("got get_props error %d", res); - - value.value = "hw:1"; - value.size = strlen (value.value)+1; - spa_props_set_prop (props, spa_props_index_for_name (props, "device"), &value); - - if ((res = spa_node_set_props (node->node, props)) < 0) - g_debug ("got set_props error %d", res); -} - -static void -get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (prop_id) { - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -#if 0 -static gboolean -on_received_buffer (PinosPort *port, - uint32_t buffer_id, - GError **error, - gpointer user_data) -{ - PinosSpaAlsaSink *this = user_data; - PinosSpaAlsaSinkPrivate *priv = this->priv; - unsigned int i; - SpaBuffer *buffer = NULL; //port->buffers[buffer_id]; - - for (i = 0; i < buffer->n_datas; i++) { - SpaData *d = SPA_BUFFER_DATAS (buffer); - SpaMemory *mem; - PinosRingbufferArea areas[2]; - uint8_t *data; - size_t size, towrite, total; - - mem = spa_memory_find (&d[i].mem.mem); - - size = d[i].mem.size; - data = SPA_MEMBER (mem->ptr, d[i].mem.offset, uint8_t); - - pinos_ringbuffer_get_write_areas (priv->ringbuffer, areas); - - total = MIN (size, areas[0].len + areas[1].len); - g_debug ("total write %zd %zd", total, areas[0].len + areas[1].len); - towrite = MIN (size, areas[0].len); - memcpy (areas[0].data, data, towrite); - size -= towrite; - data += towrite; - towrite = MIN (size, areas[1].len); - memcpy (areas[1].data, data, towrite); - - pinos_ringbuffer_write_advance (priv->ringbuffer, total); - } - - return TRUE; -} -#endif - -static void -sink_constructed (GObject * object) -{ - PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object); - - setup_node (sink); - - G_OBJECT_CLASS (pinos_spa_alsa_sink_parent_class)->constructed (object); -} - -static void -sink_finalize (GObject * object) -{ - PinosNode *node = PINOS_NODE (object); - PinosSpaAlsaSink *sink = PINOS_SPA_ALSA_SINK (object); - - g_debug ("alsa-sink %p: dispose", sink); - spa_handle_clear (node->node->handle); - g_free (node->node->handle); - - G_OBJECT_CLASS (pinos_spa_alsa_sink_parent_class)->finalize (object); -} - -static void -pinos_spa_alsa_sink_class_init (PinosSpaAlsaSinkClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (PinosSpaAlsaSinkPrivate)); - - gobject_class->constructed = sink_constructed; - gobject_class->finalize = sink_finalize; - gobject_class->get_property = get_property; - gobject_class->set_property = set_property; -} - -static void -pinos_spa_alsa_sink_init (PinosSpaAlsaSink * sink) -{ - sink->priv = PINOS_SPA_ALSA_SINK_GET_PRIVATE (sink); -} - -PinosNode * -pinos_spa_alsa_sink_new (PinosDaemon *daemon, - const gchar *name, - PinosProperties *properties) -{ - PinosNode *node; - SpaNode *n; - SpaResult res; - - if ((res = make_node (&n, - "spa/build/plugins/alsa/libspa-alsa.so", - "alsa-sink")) < 0) { - g_error ("can't create v4l2-source: %d", res); - return NULL; - } - - node = g_object_new (PINOS_TYPE_SPA_ALSA_SINK, - "daemon", daemon, - "name", name, - "properties", properties, - "node", n, - NULL); - - return node; -} diff --git a/pinos/modules/spa/spa-alsa-sink.h b/pinos/modules/spa/spa-alsa-sink.h deleted file mode 100644 index de18ea195..000000000 --- a/pinos/modules/spa/spa-alsa-sink.h +++ /dev/null @@ -1,61 +0,0 @@ -/* Pinos - * Copyright (C) 2015 Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef __PINOS_SPA_ALSA_SINK_H__ -#define __PINOS_SPA_ALSA_SINK_H__ - -#include - -#include -#include - -G_BEGIN_DECLS - -#define PINOS_TYPE_SPA_ALSA_SINK (pinos_spa_alsa_sink_get_type ()) -#define PINOS_IS_SPA_ALSA_SINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PINOS_TYPE_SPA_ALSA_SINK)) -#define PINOS_IS_SPA_ALSA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PINOS_TYPE_SPA_ALSA_SINK)) -#define PINOS_SPA_ALSA_SINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkClass)) -#define PINOS_SPA_ALSA_SINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSink)) -#define PINOS_SPA_ALSA_SINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PINOS_TYPE_SPA_ALSA_SINK, PinosSpaAlsaSinkClass)) -#define PINOS_SPA_ALSA_SINK_CAST(obj) ((PinosSpaAlsaSink*)(obj)) -#define PINOS_SPA_ALSA_SINK_CLASS_CAST(klass) ((PinosSpaAlsaSinkClass*)(klass)) - -typedef struct _PinosSpaAlsaSink PinosSpaAlsaSink; -typedef struct _PinosSpaAlsaSinkClass PinosSpaAlsaSinkClass; -typedef struct _PinosSpaAlsaSinkPrivate PinosSpaAlsaSinkPrivate; - -struct _PinosSpaAlsaSink { - PinosNode object; - - PinosSpaAlsaSinkPrivate *priv; -}; - -struct _PinosSpaAlsaSinkClass { - PinosNodeClass parent_class; -}; - -GType pinos_spa_alsa_sink_get_type (void); - -PinosNode * pinos_spa_alsa_sink_new (PinosDaemon *daemon, - const gchar *name, - PinosProperties *properties); - -G_END_DECLS - -#endif /* __PINOS_SPA_ALSA_SINK_H__ */ diff --git a/pinos/server/daemon.c b/pinos/server/daemon.c index 4f6c292c6..9d8bf10e3 100644 --- a/pinos/server/daemon.c +++ b/pinos/server/daemon.c @@ -216,7 +216,7 @@ on_link_state_notify (GObject *obj, } static void -on_port_added (PinosNode *node, PinosDirection direction, guint port_id, PinosClient *client) +on_port_added (PinosNode *node, PinosDirection direction, uint32_t port_id, PinosClient *client) { PinosDaemon *this; PinosProperties *props; @@ -231,7 +231,8 @@ on_port_added (PinosNode *node, PinosDirection direction, guint port_id, PinosCl path = pinos_properties_get (props, "pinos.target.node"); if (path) { - guint new_port; + guint target_port; + guint node_port; target = pinos_daemon_find_node (this, pinos_direction_reverse (direction), @@ -240,29 +241,33 @@ on_port_added (PinosNode *node, PinosDirection direction, guint port_id, PinosCl 0, NULL, &error); - if (target == NULL) { - pinos_node_report_error (node, error); - return; - } + if (target == NULL) + goto error; - new_port = pinos_node_get_free_port (target, pinos_direction_reverse (direction)); - if (new_port == SPA_ID_INVALID) { + target_port = pinos_node_get_free_port (target, pinos_direction_reverse (direction)); + if (target_port == SPA_ID_INVALID) { g_set_error (&error, PINOS_ERROR, PINOS_ERROR_NODE_PORT, - "can't get free port from node %s", pinos_node_get_object_path (target)); - pinos_node_report_error (node, error); - return; + "can't get free port from target %s", pinos_node_get_object_path (target)); + goto error; + } + node_port = pinos_node_get_free_port (node, direction); + if (node_port == SPA_ID_INVALID) { + g_set_error (&error, + PINOS_ERROR, + PINOS_ERROR_NODE_PORT, + "can't get free port from node %s", pinos_node_get_object_path (node)); + goto error; } - if (direction == PINOS_DIRECTION_OUTPUT) - link = pinos_node_link (node, port_id, target, new_port, NULL, NULL, &error); - else - link = pinos_node_link (target, new_port, node, port_id, NULL, NULL, &error); - if (link == NULL) { - pinos_node_report_error (node, error); - return; - } + if (direction == PINOS_DIRECTION_OUTPUT) + link = pinos_node_link (node, node_port, target, target_port, NULL, NULL, &error); + else + link = pinos_node_link (target, target_port, node, node_port, NULL, NULL, &error); + + if (link == NULL) + goto error; pinos_client_add_object (client, G_OBJECT (link)); @@ -271,10 +276,17 @@ on_port_added (PinosNode *node, PinosDirection direction, guint port_id, PinosCl g_object_unref (link); } + return; + +error: + { + pinos_node_report_error (node, error); + return; + } } static void -on_port_removed (PinosNode *node, guint port_id, PinosClient *client) +on_port_removed (PinosNode *node, uint32_t port_id, PinosClient *client) { } diff --git a/spa/lib/audio-raw.c b/spa/lib/audio-raw.c index 73ee1e6bb..51acfb06f 100644 --- a/spa/lib/audio-raw.c +++ b/spa/lib/audio-raw.c @@ -223,7 +223,7 @@ spa_format_audio_init (SpaMediaType type, SPA_AUDIO_FORMAT_S16, SPA_AUDIO_FLAG_NONE, SPA_AUDIO_LAYOUT_INTERLEAVED, - 44100, + 32000, 2, 0 }; diff --git a/spa/plugins/alsa/alsa-sink.c b/spa/plugins/alsa/alsa-sink.c index e991885d6..50ce7838f 100644 --- a/spa/plugins/alsa/alsa-sink.c +++ b/spa/plugins/alsa/alsa-sink.c @@ -24,25 +24,17 @@ #include #include -typedef struct _SpaALSASink SpaALSASink; +#include "alsa-utils.h" + +typedef struct _SpaALSAState SpaALSASink; static const char default_device[] = "default"; static const uint32_t default_buffer_time = 10000; static const uint32_t default_period_time = 5000; static const bool default_period_event = 0; -typedef struct { - SpaProps props; - char device[64]; - char device_name[128]; - char card_name[128]; - uint32_t buffer_time; - uint32_t period_time; - bool period_event; -} SpaALSASinkProps; - static void -reset_alsa_sink_props (SpaALSASinkProps *props) +reset_alsa_sink_props (SpaALSAProps *props) { strncpy (props->device, default_device, 64); props->buffer_time = default_buffer_time; @@ -50,58 +42,6 @@ reset_alsa_sink_props (SpaALSASinkProps *props) props->period_event = default_period_event; } -typedef struct { - bool opened; - bool have_buffers; - snd_pcm_t *handle; - snd_output_t *output; - snd_pcm_sframes_t buffer_size; - snd_pcm_sframes_t period_size; - snd_pcm_channel_area_t areas[16]; - bool running; - SpaPollFd fds[16]; - SpaPollItem poll; -} SpaALSAState; - -typedef struct _ALSABuffer ALSABuffer; - -struct _ALSABuffer { - SpaBuffer buffer; - SpaMeta metas[2]; - SpaMetaHeader header; - SpaMetaRingbuffer ringbuffer; - SpaData datas[1]; -}; - -struct _SpaALSASink { - SpaHandle handle; - SpaNode node; - - SpaALSASinkProps props[2]; - - SpaNodeEventCallback event_cb; - void *user_data; - - bool have_format; - SpaFormatAudio query_format; - SpaFormatAudio current_format; - - SpaALSAState state; - - SpaPortInfo info; - SpaAllocParam *params[1]; - SpaAllocParamBuffers param_buffers; - SpaPortStatus status; - - SpaBuffer *buffers; - unsigned int n_buffers; - uint32_t input_buffer; - - SpaMemory *alloc_bufmem; - SpaMemory *alloc_mem; - ALSABuffer *alloc_buffers; -}; - static void update_state (SpaALSASink *this, SpaNodeState state) { @@ -120,8 +60,6 @@ update_state (SpaALSASink *this, SpaNodeState state) this->event_cb (&this->node, &event, this->user_data); } -#include "alsa-utils.c" - static const uint32_t min_uint32 = 1; static const uint32_t max_uint32 = UINT32_MAX; @@ -142,37 +80,37 @@ enum { static const SpaPropInfo prop_info[] = { - { PROP_ID_DEVICE, offsetof (SpaALSASinkProps, device), + { PROP_ID_DEVICE, offsetof (SpaALSAProps, device), "device", "ALSA device, as defined in an asound configuration file", SPA_PROP_FLAG_READWRITE, SPA_PROP_TYPE_STRING, 63, SPA_PROP_RANGE_TYPE_NONE, 0, NULL, NULL }, - { PROP_ID_DEVICE_NAME, offsetof (SpaALSASinkProps, device_name), + { PROP_ID_DEVICE_NAME, offsetof (SpaALSAProps, device_name), "device-name", "Human-readable name of the sound device", SPA_PROP_FLAG_READABLE, SPA_PROP_TYPE_STRING, 127, SPA_PROP_RANGE_TYPE_NONE, 0, NULL, NULL }, - { PROP_ID_CARD_NAME, offsetof (SpaALSASinkProps, card_name), + { PROP_ID_CARD_NAME, offsetof (SpaALSAProps, card_name), "card-name", "Human-readable name of the sound card", SPA_PROP_FLAG_READABLE, SPA_PROP_TYPE_STRING, 127, SPA_PROP_RANGE_TYPE_NONE, 0, NULL, NULL }, - { PROP_ID_BUFFER_TIME, offsetof (SpaALSASinkProps, buffer_time), + { PROP_ID_BUFFER_TIME, offsetof (SpaALSAProps, buffer_time), "buffer-time", "The total size of the buffer in time", SPA_PROP_FLAG_READWRITE, SPA_PROP_TYPE_UINT32, sizeof (uint32_t), SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range, NULL }, - { PROP_ID_PERIOD_TIME, offsetof (SpaALSASinkProps, period_time), + { PROP_ID_PERIOD_TIME, offsetof (SpaALSAProps, period_time), "period-time", "The size of a period in time", SPA_PROP_FLAG_READWRITE, SPA_PROP_TYPE_UINT32, sizeof (uint32_t), SPA_PROP_RANGE_TYPE_MIN_MAX, 2, uint32_range, NULL }, - { PROP_ID_PERIOD_EVENT, offsetof (SpaALSASinkProps, period_event), + { PROP_ID_PERIOD_EVENT, offsetof (SpaALSAProps, period_event), "period-event", "Generate an event each period", SPA_PROP_FLAG_READWRITE, SPA_PROP_TYPE_BOOL, sizeof (bool), @@ -202,7 +140,7 @@ spa_alsa_sink_node_set_props (SpaNode *node, const SpaProps *props) { SpaALSASink *this; - SpaALSASinkProps *p; + SpaALSAProps *p; SpaResult res; if (node == NULL || node->handle == NULL) @@ -394,7 +332,7 @@ spa_alsa_sink_node_port_set_format (SpaNode *node, if ((res = spa_format_audio_parse (format, &this->current_format)) < 0) return res; - if (alsa_set_format (this, &this->current_format, false) < 0) + if (spa_alsa_set_format (this, &this->current_format, false) < 0) return SPA_RESULT_ERROR; this->info.flags = SPA_PORT_INFO_FLAG_CAN_ALLOC_BUFFERS; @@ -495,9 +433,10 @@ spa_alsa_sink_node_port_alloc_buffers (SpaNode *node, uint32_t *n_buffers) { SpaALSASink *this; - ALSABuffer *b; - SpaALSAState *state; + SpaALSABuffer *b; unsigned int i, n_bufs; + size_t buffer_size; + uint8_t *bufmem; if (node == NULL || node->handle == NULL || buffers == NULL) return SPA_RESULT_INVALID_ARGUMENTS; @@ -510,28 +449,30 @@ spa_alsa_sink_node_port_alloc_buffers (SpaNode *node, if (!this->have_format) return SPA_RESULT_NO_FORMAT; - state = &this->state; - n_bufs = *n_buffers; + buffer_size = this->buffer_frames * this->frame_size; + if (!this->alloc_bufmem) - this->alloc_bufmem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED, NULL, state->buffer_size * n_bufs); + this->alloc_bufmem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED, NULL, buffer_size * n_bufs); + bufmem = spa_memory_ensure_ptr (this->alloc_bufmem); if (!this->alloc_mem) - this->alloc_mem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED, NULL, sizeof (ALSABuffer) * n_bufs); + this->alloc_mem = spa_memory_alloc_with_fd (SPA_MEMORY_POOL_SHARED, NULL, sizeof (SpaALSABuffer) * n_bufs); this->alloc_buffers = spa_memory_ensure_ptr (this->alloc_mem); + this->n_buffers = n_bufs; for (i = 0; i < n_bufs; i++) { b = &this->alloc_buffers[i]; b->buffer.id = i; b->buffer.mem.mem = this->alloc_mem->mem; - b->buffer.mem.offset = sizeof (ALSABuffer) * i; - b->buffer.mem.size = sizeof (ALSABuffer); + b->buffer.mem.offset = sizeof (SpaALSABuffer) * i; + b->buffer.mem.size = sizeof (SpaALSABuffer); b->buffer.n_metas = 2; - b->buffer.metas = offsetof (ALSABuffer, metas); + b->buffer.metas = offsetof (SpaALSABuffer, metas); b->buffer.n_datas = 1; - b->buffer.datas = offsetof (ALSABuffer, datas); + b->buffer.datas = offsetof (SpaALSABuffer, datas); b->header.flags = 0; b->header.seq = 0; @@ -539,7 +480,7 @@ spa_alsa_sink_node_port_alloc_buffers (SpaNode *node, b->header.dts_offset = 0; b->metas[0].type = SPA_META_TYPE_HEADER; - b->metas[0].offset = offsetof (ALSABuffer, header); + b->metas[0].offset = offsetof (SpaALSABuffer, header); b->metas[0].size = sizeof (b->header); b->ringbuffer.readindex = 0; @@ -548,13 +489,14 @@ spa_alsa_sink_node_port_alloc_buffers (SpaNode *node, b->ringbuffer.size_mask = 0; b->metas[1].type = SPA_META_TYPE_RINGBUFFER; - b->metas[1].offset = offsetof (ALSABuffer, ringbuffer); + b->metas[1].offset = offsetof (SpaALSABuffer, ringbuffer); b->metas[1].size = sizeof (b->ringbuffer); b->datas[0].mem.mem = this->alloc_bufmem->mem; - b->datas[0].mem.offset = state->buffer_size * i; - b->datas[0].mem.size = state->buffer_size; + b->datas[0].mem.offset = buffer_size * i; + b->datas[0].mem.size = buffer_size; b->datas[0].stride = 0; + b->ptr = bufmem + buffer_size * i; buffers[i] = &b->buffer; } @@ -621,12 +563,13 @@ spa_alsa_sink_node_port_push_input (SpaNode *node, continue; } - if (this->input_buffer != -1) { + if (this->ready_head != NULL) { info[i].status = SPA_RESULT_HAVE_ENOUGH_INPUT; have_enough = true; continue; } - this->input_buffer = info[i].buffer_id; + + this->ready_head = &this->alloc_buffers[info[i].buffer_id]; } info[i].status = SPA_RESULT_OK; } @@ -719,6 +662,7 @@ alsa_sink_init (const SpaHandleFactory *factory, this->node.handle = handle; this->props[1].props.n_prop_info = PROP_ID_LAST; this->props[1].props.prop_info = prop_info; + this->stream = SND_PCM_STREAM_PLAYBACK; reset_alsa_sink_props (&this->props[1]); this->status.flags = SPA_PORT_STATUS_FLAG_NEED_INPUT; diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c index 0528213df..43974a7c7 100644 --- a/spa/plugins/alsa/alsa-utils.c +++ b/spa/plugins/alsa/alsa-utils.c @@ -7,26 +7,27 @@ #include #include +#include "alsa-utils.h" + static int verbose = 0; /* verbose flag */ #define CHECK(s,msg) if ((err = (s)) < 0) { printf (msg ": %s\n", snd_strerror(err)); return err; } static int -spa_alsa_open (SpaALSASink *this) +spa_alsa_open (SpaALSAState *state) { - SpaALSAState *state = &this->state; int err; - SpaALSASinkProps *props = &this->props[1]; + SpaALSAProps *props = &state->props[1]; if (state->opened) return 0; CHECK (snd_output_stdio_attach (&state->output, stderr, 0), "attach failed"); - printf ("Playback device open '%s'\n", props->device); - CHECK (snd_pcm_open (&state->handle, + printf ("ALSA device open '%s'\n", props->device); + CHECK (snd_pcm_open (&state->hndl, props->device, - SND_PCM_STREAM_PLAYBACK, + state->stream, SND_PCM_NONBLOCK | SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | @@ -38,16 +39,15 @@ spa_alsa_open (SpaALSASink *this) } static int -spa_alsa_close (SpaALSASink *this) +spa_alsa_close (SpaALSAState *state) { - SpaALSAState *state = &this->state; int err = 0; if (!state->opened) return 0; printf ("Playback device closing\n"); - CHECK (snd_pcm_close (state->handle), "close failed"); + CHECK (snd_pcm_close (state->hndl), "close failed"); state->opened = false; @@ -105,93 +105,94 @@ spa_alsa_format_to_alsa (SpaAudioFormat format) return SND_PCM_FORMAT_UNKNOWN; } -static int -alsa_set_format (SpaALSASink *this, SpaFormatAudio *fmt, bool try_only) +int +spa_alsa_set_format (SpaALSAState *state, SpaFormatAudio *fmt, bool try_only) { unsigned int rrate; snd_pcm_uframes_t size; int err, dir; snd_pcm_hw_params_t *params; snd_pcm_format_t format; - SpaALSAState *state = &this->state; SpaAudioInfoRaw *info = &fmt->info.raw; - snd_pcm_t *handle; + snd_pcm_t *hndl; unsigned int buffer_time; unsigned int period_time; - SpaALSASinkProps *props = &this->props[1]; + SpaALSAProps *props = &state->props[1]; - if ((err = spa_alsa_open (this)) < 0) + if ((err = spa_alsa_open (state)) < 0) return err; - handle = state->handle; + hndl = state->hndl; snd_pcm_hw_params_alloca (¶ms); /* choose all parameters */ - CHECK (snd_pcm_hw_params_any (handle, params), "Broken configuration for playback: no configurations available"); + CHECK (snd_pcm_hw_params_any (hndl, params), "Broken configuration for playback: no configurations available"); /* set hardware resampling */ - CHECK (snd_pcm_hw_params_set_rate_resample (handle, params, 0), "set_rate_resample"); + CHECK (snd_pcm_hw_params_set_rate_resample (hndl, params, 0), "set_rate_resample"); /* set the interleaved read/write format */ - CHECK (snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set_access"); + CHECK (snd_pcm_hw_params_set_access(hndl, params, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set_access"); /* set the sample format */ format = spa_alsa_format_to_alsa (info->format); printf ("Stream parameters are %iHz, %s, %i channels\n", info->rate, snd_pcm_format_name(format), info->channels); - CHECK (snd_pcm_hw_params_set_format (handle, params, format), "set_format"); + CHECK (snd_pcm_hw_params_set_format (hndl, params, format), "set_format"); /* set the count of channels */ - CHECK (snd_pcm_hw_params_set_channels (handle, params, info->channels), "set_channels"); + CHECK (snd_pcm_hw_params_set_channels (hndl, params, info->channels), "set_channels"); /* set the stream rate */ rrate = info->rate; - CHECK (snd_pcm_hw_params_set_rate_near (handle, params, &rrate, 0), "set_rate_near"); + CHECK (snd_pcm_hw_params_set_rate_near (hndl, params, &rrate, 0), "set_rate_near"); if (rrate != info->rate) { printf("Rate doesn't match (requested %iHz, get %iHz)\n", info->rate, rrate); return -EINVAL; } + + state->frame_size = info->channels * 2; + /* set the buffer time */ buffer_time = props->buffer_time; - CHECK (snd_pcm_hw_params_set_buffer_time_near (handle, params, &buffer_time, &dir), "set_buffer_time_near"); + CHECK (snd_pcm_hw_params_set_buffer_time_near (hndl, params, &buffer_time, &dir), "set_buffer_time_near"); CHECK (snd_pcm_hw_params_get_buffer_size (params, &size), "get_buffer_size"); - state->buffer_size = size; + state->buffer_frames = size; /* set the period time */ period_time = props->period_time; - CHECK (snd_pcm_hw_params_set_period_time_near (handle, params, &period_time, &dir), "set_period_time_near"); + CHECK (snd_pcm_hw_params_set_period_time_near (hndl, params, &period_time, &dir), "set_period_time_near"); CHECK (snd_pcm_hw_params_get_period_size (params, &size, &dir), "get_period_size"); - state->period_size = size; + state->period_frames = size; /* write the parameters to device */ - CHECK (snd_pcm_hw_params (handle, params), "set_hw_params"); + CHECK (snd_pcm_hw_params (hndl, params), "set_hw_params"); return 0; } static int -set_swparams (SpaALSASink *this) +set_swparams (SpaALSAState *state) { - SpaALSAState *state = &this->state; - snd_pcm_t *handle = state->handle; + snd_pcm_t *hndl = state->hndl; int err = 0; snd_pcm_sw_params_t *params; - SpaALSASinkProps *props = &this->props[1]; + SpaALSAProps *props = &state->props[1]; snd_pcm_sw_params_alloca (¶ms); /* get the current params */ - CHECK (snd_pcm_sw_params_current (handle, params), "sw_params_current"); + CHECK (snd_pcm_sw_params_current (hndl, params), "sw_params_current"); /* start the transfer when the buffer is almost full: */ - /* (buffer_size / avail_min) * avail_min */ - CHECK (snd_pcm_sw_params_set_start_threshold (handle, params, - (state->buffer_size / state->period_size) * state->period_size), "set_start_threshold"); + /* (buffer_frames / avail_min) * avail_min */ + CHECK (snd_pcm_sw_params_set_start_threshold (hndl, params, + (state->buffer_frames / state->period_frames) * state->period_frames), "set_start_threshold"); /* allow the transfer when at least period_size samples can be processed */ /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */ - CHECK (snd_pcm_sw_params_set_avail_min (handle, params, - props->period_event ? state->buffer_size : state->period_size), "set_avail_min"); + CHECK (snd_pcm_sw_params_set_avail_min (hndl, params, + props->period_event ? state->buffer_frames : state->period_frames), "set_avail_min"); /* enable period events when requested */ if (props->period_event) { - CHECK (snd_pcm_sw_params_set_period_event (handle, params, 1), "set_period_event"); + CHECK (snd_pcm_sw_params_set_period_event (hndl, params, 1), "set_period_event"); } /* write the parameters to the playback device */ - CHECK (snd_pcm_sw_params (handle, params), "sw_params"); + CHECK (snd_pcm_sw_params (hndl, params), "sw_params"); return 0; } @@ -200,20 +201,20 @@ set_swparams (SpaALSASink *this) * Underrun and suspend recovery */ static int -xrun_recovery (snd_pcm_t *handle, int err) +xrun_recovery (snd_pcm_t *hndl, int err) { if (verbose) printf("stream recovery\n"); if (err == -EPIPE) { /* under-run */ - err = snd_pcm_prepare(handle); + err = snd_pcm_prepare(hndl); if (err < 0) printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); return 0; } else if (err == -ESTRPIPE) { - while ((err = snd_pcm_resume(handle)) == -EAGAIN) + while ((err = snd_pcm_resume(hndl)) == -EAGAIN) sleep(1); /* wait until the suspend flag is released */ if (err < 0) { - err = snd_pcm_prepare(handle); + err = snd_pcm_prepare(hndl); if (err < 0) printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); } @@ -223,7 +224,7 @@ xrun_recovery (snd_pcm_t *handle, int err) } static void -pull_input (SpaALSASink *this, void *data, snd_pcm_uframes_t frames) +pull_input (SpaALSAState *state, void *data, snd_pcm_uframes_t frames) { SpaNodeEvent event; SpaNodeEventNeedInput ni; @@ -232,21 +233,20 @@ pull_input (SpaALSASink *this, void *data, snd_pcm_uframes_t frames) event.size = sizeof (ni); event.data = ∋ ni.port_id = 0; - this->event_cb (&this->node, &event, this->user_data); + state->event_cb (&state->node, &event, state->user_data); } static int -mmap_write (SpaALSASink *this) +mmap_write (SpaALSAState *state) { - SpaALSAState *state = &this->state; - snd_pcm_t *handle = state->handle; + snd_pcm_t *hndl = state->hndl; int err; snd_pcm_sframes_t avail, commitres; snd_pcm_uframes_t offset, frames, size; const snd_pcm_channel_area_t *my_areas; - if ((avail = snd_pcm_avail_update (handle)) < 0) { - if ((err = xrun_recovery (handle, avail)) < 0) { + if ((avail = snd_pcm_avail_update (hndl)) < 0) { + if ((err = xrun_recovery (hndl, avail)) < 0) { printf ("Write error: %s\n", snd_strerror (err)); return -1; } @@ -255,27 +255,20 @@ mmap_write (SpaALSASink *this) size = avail; while (size > 0) { frames = size; - if ((err = snd_pcm_mmap_begin (handle, &my_areas, &offset, &frames)) < 0) { - if ((err = xrun_recovery(handle, err)) < 0) { + if ((err = snd_pcm_mmap_begin (hndl, &my_areas, &offset, &frames)) < 0) { + if ((err = xrun_recovery(hndl, err)) < 0) { printf("MMAP begin avail error: %s\n", snd_strerror(err)); return -1; } } - pull_input (this, + pull_input (state, (uint8_t *)my_areas[0].addr + (offset * sizeof (uint16_t) * 2), frames); - if (this->input_buffer != SPA_ID_INVALID) { - if (this->input_buffer != this->alloc_buffers[0].buffer.id) { - /* FIXME, copy input */ - } - this->input_buffer = SPA_ID_INVALID; - } - - commitres = snd_pcm_mmap_commit (handle, offset, frames); + commitres = snd_pcm_mmap_commit (hndl, offset, frames); if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { - if ((err = xrun_recovery (handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { + if ((err = xrun_recovery (hndl, commitres >= 0 ? -EPIPE : commitres)) < 0) { printf("MMAP commit error: %s\n", snd_strerror(err)); return -1; } @@ -285,25 +278,111 @@ mmap_write (SpaALSASink *this) return 0; } +static int +mmap_read (SpaALSAState *state) +{ + snd_pcm_t *hndl = state->hndl; + int err; + snd_pcm_sframes_t avail, commitres; + snd_pcm_uframes_t offset, frames, size; + snd_pcm_status_t *status; + const snd_pcm_channel_area_t *my_areas; + SpaALSABuffer *b; + snd_htimestamp_t htstamp = { 0, 0 }; + int64_t now; + uint8_t *dest; + + snd_pcm_status_alloca(&status); + + if ((err = snd_pcm_status (hndl, status)) < 0) + return err; + + avail = snd_pcm_status_get_avail (status); + snd_pcm_status_get_htstamp (status, &htstamp); + now = (int64_t)htstamp.tv_sec * 1000000000ll + (int64_t)htstamp.tv_nsec; + + b = state->free_head; + if (b == NULL) + fprintf (stderr, "no more buffers\n"); + else { + state->free_head = b->next; + if (state->free_head == NULL) + state->free_tail = NULL; + b->next = NULL; + dest = b->ptr; + } + + size = avail; + while (size > 0) { + frames = size; + if ((err = snd_pcm_mmap_begin (hndl, &my_areas, &offset, &frames)) < 0) { + if ((err = xrun_recovery(hndl, err)) < 0) { + printf("MMAP begin avail error: %s\n", snd_strerror (err)); + return -1; + } + } + + if (b) { + size_t n_bytes = frames * state->frame_size; + + memcpy (dest, + (uint8_t *)my_areas[0].addr + (offset * state->frame_size), + n_bytes); + dest += n_bytes; + } + + commitres = snd_pcm_mmap_commit (hndl, offset, frames); + if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { + if ((err = xrun_recovery (hndl, commitres >= 0 ? -EPIPE : commitres)) < 0) { + printf("MMAP commit error: %s\n", snd_strerror(err)); + return -1; + } + } + size -= frames; + } + + if (b) { + SpaNodeEvent event; + SpaNodeEventHaveOutput ho; + SpaData *d; + + d = SPA_BUFFER_DATAS (b->outbuf); + d[0].mem.size = avail * state->frame_size; + + if (state->ready_tail) + state->ready_tail->next = b; + state->ready_tail = b; + if (state->ready_head == NULL) + state->ready_head = b; + state->ready_count++; + + event.type = SPA_NODE_EVENT_TYPE_HAVE_OUTPUT; + event.size = sizeof (ho); + event.data = &ho; + ho.port_id = 0; + state->event_cb (&state->node, &event, state->user_data); + } + return 0; +} + static int alsa_on_fd_events (SpaPollNotifyData *data) { - SpaALSASink *this = data->user_data; - SpaALSAState *state = &this->state; - snd_pcm_t *handle = state->handle; + SpaALSAState *state = data->user_data; + snd_pcm_t *hndl = state->hndl; int err; unsigned short revents; - snd_pcm_poll_descriptors_revents (handle, + snd_pcm_poll_descriptors_revents (hndl, (struct pollfd *)data->fds, data->n_fds, &revents); if (revents & POLLERR) { - if (snd_pcm_state (handle) == SND_PCM_STATE_XRUN || - snd_pcm_state (handle) == SND_PCM_STATE_SUSPENDED) { - err = snd_pcm_state (handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE; - if ((err = xrun_recovery (handle, err)) < 0) { - printf ("Write error: %s\n", snd_strerror(err)); + if (snd_pcm_state (hndl) == SND_PCM_STATE_XRUN || + snd_pcm_state (hndl) == SND_PCM_STATE_SUSPENDED) { + err = snd_pcm_state (hndl) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE; + if ((err = xrun_recovery (hndl, err)) < 0) { + printf ("error: %s\n", snd_strerror (err)); return -1; } } else { @@ -311,36 +390,43 @@ alsa_on_fd_events (SpaPollNotifyData *data) return -1; } } - if (!(revents & POLLOUT)) - return -1; - mmap_write (this); + if (state->stream == SND_PCM_STREAM_CAPTURE) { + if (!(revents & POLLIN)) + return -1; + + mmap_read (state); + } else { + if (!(revents & POLLOUT)) + return -1; + + mmap_write (state); + } return 0; } -static int -spa_alsa_start (SpaALSASink *this) +int +spa_alsa_start (SpaALSAState *state) { - SpaALSAState *state = &this->state; int err; SpaNodeEvent event; - if (spa_alsa_open (this) < 0) + if (spa_alsa_open (state) < 0) return -1; if (!state->have_buffers) return -1; - CHECK (set_swparams (this), "swparams"); + CHECK (set_swparams (state), "swparams"); - snd_pcm_dump (state->handle, state->output); + snd_pcm_dump (state->hndl, state->output); - if ((state->poll.n_fds = snd_pcm_poll_descriptors_count (state->handle)) <= 0) { + if ((state->poll.n_fds = snd_pcm_poll_descriptors_count (state->hndl)) <= 0) { printf ("Invalid poll descriptors count\n"); return state->poll.n_fds; } - if ((err = snd_pcm_poll_descriptors (state->handle, (struct pollfd *)state->fds, state->poll.n_fds)) < 0) { + if ((err = snd_pcm_poll_descriptors (state->hndl, (struct pollfd *)state->fds, state->poll.n_fds)) < 0) { printf ("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err)); return err; } @@ -355,32 +441,31 @@ spa_alsa_start (SpaALSASink *this) state->poll.idle_cb = NULL; state->poll.before_cb = NULL; state->poll.after_cb = alsa_on_fd_events; - state->poll.user_data = this; - this->event_cb (&this->node, &event, this->user_data); + state->poll.user_data = state; + state->event_cb (&state->node, &event, state->user_data); - mmap_write (this); - err = snd_pcm_start (state->handle); + mmap_write (state); + err = snd_pcm_start (state->hndl); return err; } -static int -spa_alsa_stop (SpaALSASink *this) +int +spa_alsa_stop (SpaALSAState *state) { - SpaALSAState *state = &this->state; SpaNodeEvent event; if (!state->opened) return 0; - snd_pcm_drop (state->handle); + snd_pcm_drop (state->hndl); event.type = SPA_NODE_EVENT_TYPE_REMOVE_POLL; event.data = &state->poll; event.size = sizeof (state->poll); - this->event_cb (&this->node, &event, this->user_data); + state->event_cb (&state->node, &event, state->user_data); - spa_alsa_close (this); + spa_alsa_close (state); return 0; } diff --git a/spa/plugins/alsa/alsa.c b/spa/plugins/alsa/alsa.c index 06db5f750..86e1e835a 100644 --- a/spa/plugins/alsa/alsa.c +++ b/spa/plugins/alsa/alsa.c @@ -20,7 +20,9 @@ #include #include +extern const SpaHandleFactory spa_alsa_source_factory; extern const SpaHandleFactory spa_alsa_sink_factory; +extern const SpaHandleFactory spa_alsa_monitor_factory; SpaResult spa_enum_handle_factory (const SpaHandleFactory **factory, @@ -35,8 +37,14 @@ spa_enum_handle_factory (const SpaHandleFactory **factory, switch (index) { case 0: + *factory = &spa_alsa_source_factory; + break; + case 1: *factory = &spa_alsa_sink_factory; break; + case 2: + *factory = &spa_alsa_monitor_factory; + break; default: return SPA_RESULT_ENUM_END; } diff --git a/spa/plugins/alsa/meson.build b/spa/plugins/alsa/meson.build index ceddaf72f..272b2fdc4 100644 --- a/spa/plugins/alsa/meson.build +++ b/spa/plugins/alsa/meson.build @@ -1,9 +1,12 @@ alsa_sources = ['alsa.c', - 'alsa-sink.c'] + 'alsa-monitor.c', + 'alsa-sink.c', + 'alsa-source.c', + 'alsa-utils.c'] alsalib = shared_library('spa-alsa', alsa_sources, include_directories : inc, - dependencies : alsa_dep, + dependencies : [ alsa_dep, libudev_dep ], link_with : spalib, install : true)