From 4b2520d173ce31a06112b205aa6438dc4ab7ab56 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 3 Jun 2016 19:05:19 +0200 Subject: [PATCH] Split out header files Use separate header files Add pull-based alsasink Add audiotestsrc Implement negotiation and scheduling of audiotestsrc ! alsasink --- configure.ac | 7 + pinos/Makefile.am | 6 +- pinos/spi/buffer.h | 17 +- pinos/spi/command.h | 55 ++ pinos/spi/{result.h => defs.h} | 35 +- pinos/spi/event.h | 57 ++ pinos/spi/node.h | 176 +++--- pinos/spi/params.h | 30 +- pinos/spi/port.h | 91 ++++ pinos/tests/alsa-utils.c | 448 ++++++++++++++++ pinos/tests/spi-alsa-sink.c | 953 +++++++++++++++++++++++++++++++++ pinos/tests/spi-audiotestsrc.c | 863 +++++++++++++++++++++++++++++ pinos/tests/spi-plugins.h | 22 + pinos/tests/spi-volume.c | 157 +++--- pinos/tests/test-node.c | 239 ++++++--- 15 files changed, 2869 insertions(+), 287 deletions(-) create mode 100644 pinos/spi/command.h rename pinos/spi/{result.h => defs.h} (74%) create mode 100644 pinos/spi/event.h create mode 100644 pinos/spi/port.h create mode 100644 pinos/tests/alsa-utils.c create mode 100644 pinos/tests/spi-alsa-sink.c create mode 100644 pinos/tests/spi-audiotestsrc.c create mode 100644 pinos/tests/spi-plugins.h diff --git a/configure.ac b/configure.ac index 14fedf3ec..b79a93065 100644 --- a/configure.ac +++ b/configure.ac @@ -229,6 +229,13 @@ PKG_CHECK_MODULES(GSTALLOC, gstreamer-allocators-1.0 >= 1.5.0, dummy=yes, GST_CFLAGS="$GST_CFLAGS $GSTALLOC_CFLAGS" GST_LIBS="$GST_LIBS $GSTALLOC_LIBS" +#### ALSA #### +PKG_CHECK_MODULES(ALSA, alsa >= 0.9.1, dummy=yes, + AC_MSG_ERROR(ALSA >= 0.9.1 is required)) + +AC_SUBST(ALSA_CFLAGS) +AC_SUBST(ALSA_LIBS) + #### Build and Install man pages #### AC_ARG_ENABLE([manpages], diff --git a/pinos/Makefile.am b/pinos/Makefile.am index ea46fc186..2166b935e 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -143,10 +143,10 @@ test_client_CFLAGS = $(AM_CFLAGS) test_client_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la test_client_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) -test_node_SOURCES = tests/test-node.c -test_node_CFLAGS = $(AM_CFLAGS) +test_node_SOURCES = tests/test-node.c tests/spi-volume.c tests/spi-alsa-sink.c tests/spi-audiotestsrc.c +test_node_CFLAGS = $(AM_CFLAGS) $(ALSA_CFLAGS) test_node_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la -test_node_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) +test_node_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) $(ALSA_LIBS) ################################### # Tools programs # diff --git a/pinos/spi/buffer.h b/pinos/spi/buffer.h index 595115f39..16dce1153 100644 --- a/pinos/spi/buffer.h +++ b/pinos/spi/buffer.h @@ -20,15 +20,14 @@ #ifndef __SPI_BUFFER_H__ #define __SPI_BUFFER_H__ -G_BEGIN_DECLS - -#include - -#include -#include +#ifdef __cplusplus +extern "C" { +#endif typedef struct _SpiBuffer SpiBuffer; +#include + typedef enum { SPI_META_TYPE_INVALID = 0, SPI_META_TYPE_HEADER, @@ -118,12 +117,16 @@ spi_buffer_unref (SpiBuffer *buffer) { if (buffer != NULL) { if (--buffer->refcount == 0) { - buffer->notify (buffer); + if (buffer->notify) + buffer->notify (buffer); return NULL; } } return buffer; } +#ifdef __cplusplus +} /* extern "C" */ +#endif #endif /* __SPI_BUFFER_H__ */ diff --git a/pinos/spi/command.h b/pinos/spi/command.h new file mode 100644 index 000000000..5905d7226 --- /dev/null +++ b/pinos/spi/command.h @@ -0,0 +1,55 @@ +/* Simple Plugin Interface + * Copyright (C) 2016 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 __SPI_COMMAND_H__ +#define __SPI_COMMAND_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _SpiCommand SpiCommand; + +#include + +typedef enum { + SPI_COMMAND_INVALID = 0, + SPI_COMMAND_ACTIVATE, + SPI_COMMAND_DEACTIVATE, + SPI_COMMAND_START, + SPI_COMMAND_STOP, + SPI_COMMAND_FLUSH, + SPI_COMMAND_DRAIN, + SPI_COMMAND_MARKER, +} SpiCommandType; + +struct _SpiCommand { + volatile int refcount; + SpiNotify notify; + SpiCommandType type; + uint32_t port_id; + void *data; + size_t size; +}; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPI_COMMAND_H__ */ diff --git a/pinos/spi/result.h b/pinos/spi/defs.h similarity index 74% rename from pinos/spi/result.h rename to pinos/spi/defs.h index 82148533e..3b3cf704a 100644 --- a/pinos/spi/result.h +++ b/pinos/spi/defs.h @@ -17,10 +17,16 @@ * Boston, MA 02110-1301, USA. */ -#ifndef __SPI_RESULT_H__ -#define __SPI_RESULT_H__ +#ifndef __SPI_DEFS_H__ +#define __SPI_DEFS_H__ -G_BEGIN_DECLS +#ifdef __cplusplus +extern "C" { +#else +#include +#endif +#include +#include typedef enum { SPI_RESULT_OK = 0, @@ -38,17 +44,26 @@ typedef enum { SPI_RESULT_NOT_IMPLEMENTED = -12, SPI_RESULT_INVALID_PARAM_ID = -13, SPI_RESULT_PARAM_UNSET = -14, - SPI_RESULT_NO_MORE_FORMATS = -15, + SPI_RESULT_ENUM_END = -15, SPI_RESULT_WRONG_PARAM_TYPE = -16, - SPI_RESULT_INVALID_MEDIA_TYPE = -17, - SPI_RESULT_INVALID_FORMAT_PARAMS = -18, - SPI_RESULT_FORMAT_INCOMPLETE = -19, - SPI_RESULT_NO_MORE_PARAM_INFO = -20, + SPI_RESULT_WRONG_PARAM_SIZE = -17, + SPI_RESULT_INVALID_MEDIA_TYPE = -18, + SPI_RESULT_INVALID_FORMAT_PARAMS = -19, + SPI_RESULT_FORMAT_INCOMPLETE = -20, SPI_RESULT_INVALID_ARGUMENTS = -21, } SpiResult; +typedef enum { + SPI_DIRECTION_INVALID = 0, + SPI_DIRECTION_INPUT, + SPI_DIRECTION_OUTPUT +} SpiDirection; + typedef void (*SpiNotify) (void *data); -G_END_DECLS +#ifdef __cplusplus +} /* extern "C" */ +#endif -#endif /* __SPI_RESULT_H__ */ + +#endif /* __SPI_DEFS_H__ */ diff --git a/pinos/spi/event.h b/pinos/spi/event.h new file mode 100644 index 000000000..79fdd2cd0 --- /dev/null +++ b/pinos/spi/event.h @@ -0,0 +1,57 @@ +/* Simple Plugin Interface + * Copyright (C) 2016 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 __SPI_EVENT_H__ +#define __SPI_EVENT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _SpiEvent SpiEvent; + +#include + +typedef enum { + SPI_EVENT_TYPE_INVALID = 0, + SPI_EVENT_TYPE_ACTIVATED, + SPI_EVENT_TYPE_DEACTIVATED, + SPI_EVENT_TYPE_HAVE_OUTPUT, + SPI_EVENT_TYPE_NEED_INPUT, + SPI_EVENT_TYPE_REQUEST_DATA, + SPI_EVENT_TYPE_DRAINED, + SPI_EVENT_TYPE_MARKER, + SPI_EVENT_TYPE_ERROR, + SPI_EVENT_TYPE_BUFFERING, +} SpiEventType; + +struct _SpiEvent { + volatile int refcount; + SpiNotify notify; + SpiEventType type; + uint32_t port_id; + void *data; + size_t size; +}; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __SPI_EVENT_H__ */ diff --git a/pinos/spi/node.h b/pinos/spi/node.h index fa6f24f43..43c4d95cf 100644 --- a/pinos/spi/node.h +++ b/pinos/spi/node.h @@ -20,94 +20,18 @@ #ifndef __SPI_NODE_H__ #define __SPI_NODE_H__ -G_BEGIN_DECLS - -#include - -#include -#include -#include +#ifdef __cplusplus +extern "C" { +#endif typedef struct _SpiNode SpiNode; -typedef struct _SpiEvent SpiEvent; -/** - * SpiPortInfoFlags: - * @SPI_PORT_INFO_FLAG_NONE: no flags - * @SPI_PORT_INFO_FLAG_REMOVABLE: port can be removed - * @SPI_PORT_INFO_FLAG_OPTIONAL: processing on port is optional - * @SPI_PORT_INFO_FLAG_CAN_GIVE_BUFFER: the port can give a buffer - * @SPI_PORT_INFO_FLAG_CAN_USE_BUFFER: the port can use a provided buffer - * @SPI_PORT_INFO_FLAG_IN_PLACE: the port can process data in-place and will need - * a writable input buffer when no output buffer is specified. - * @SPI_PORT_INFO_FLAG_NO_REF: the port does not keep a ref on the buffer - */ -typedef enum { - SPI_PORT_INFO_FLAG_NONE = 0, - SPI_PORT_INFO_FLAG_REMOVABLE = 1 << 0, - SPI_PORT_INFO_FLAG_OPTIONAL = 1 << 1, - SPI_PORT_INFO_FLAG_CAN_GIVE_BUFFER = 1 << 2, - SPI_PORT_INFO_FLAG_CAN_USE_BUFFER = 1 << 3, - SPI_PORT_INFO_FLAG_IN_PLACE = 1 << 4, - SPI_PORT_INFO_FLAG_NO_REF = 1 << 5, -} SpiPortInfoFlags; - -/** - * SpiPortInfo - * @flags: extra port flags - * @size: minimum size of the buffers or 0 when not specified - * @align: required alignment of the data - * @maxbuffering: the maximum amount of bytes that the element will keep - * around internally - * @latency: latency on this port in nanoseconds - * @features: NULL terminated array of extra port features - * - */ -typedef struct { - SpiPortInfoFlags flags; - size_t minsize; - uint32_t align; - unsigned int maxbuffering; - uint64_t latency; - const char **features; -} SpiPortInfo; - -/** - * SpiPortStatusFlags: - * @SPI_PORT_STATUS_FLAG_NONE: no status flags - * @SPI_PORT_STATUS_FLAG_HAVE_OUTPUT: port has output - * @SPI_PORT_STATUS_FLAG_NEED_INPUT: port needs input - */ -typedef enum { - SPI_PORT_STATUS_FLAG_NONE = 0, - SPI_PORT_STATUS_FLAG_HAVE_OUTPUT = 1 << 0, - SPI_PORT_STATUS_FLAG_NEED_INPUT = 1 << 1, -} SpiPortStatusFlags; - -typedef struct { - SpiPortStatusFlags flags; -} SpiPortStatus; - -typedef enum { - SPI_EVENT_TYPE_INVALID = 0, - SPI_EVENT_TYPE_ACTIVATED, - SPI_EVENT_TYPE_DEACTIVATED, - SPI_EVENT_TYPE_HAVE_OUTPUT, - SPI_EVENT_TYPE_NEED_INPUT, - SPI_EVENT_TYPE_REQUEST_DATA, - SPI_EVENT_TYPE_DRAINED, - SPI_EVENT_TYPE_MARKER, - SPI_EVENT_TYPE_ERROR, -} SpiEventType; - -struct _SpiEvent { - volatile int refcount; - SpiNotify notify; - SpiEventType type; - uint32_t port_id; - void *data; - size_t size; -}; +#include +#include +#include +#include +#include +#include /** * SpiDataFlags: @@ -141,36 +65,25 @@ typedef struct { SpiEvent *event; } SpiDataInfo; -typedef enum { - SPI_COMMAND_INVALID = 0, - SPI_COMMAND_ACTIVATE, - SPI_COMMAND_DEACTIVATE, - SPI_COMMAND_START, - SPI_COMMAND_STOP, - SPI_COMMAND_FLUSH, - SPI_COMMAND_DRAIN, - SPI_COMMAND_MARKER, -} SpiCommandType; - -typedef struct { - volatile int refcount; - SpiNotify notify; - SpiCommandType type; - uint32_t port_id; - void *data; - size_t size; -} SpiCommand; - -typedef enum { - SPI_DIRECTION_INVALID = 0, - SPI_DIRECTION_INPUT, - SPI_DIRECTION_OUTPUT -} SpiDirection; - typedef void (*SpiEventCallback) (SpiNode *node, SpiEvent *event, void *user_data); +/** + * SpiInterfaceInfo: + * @interface_id: the id of the interface, can be used to get the interface + * @name: name of the interface + * @description: Human readable description of the interface. + * + * This structure lists the information about available interfaces on + * objects. + */ +typedef struct { + uint32_t interface_id; + const char *name; + const char *description; +} SpiInterfaceInfo; + /** * SpiNode: * @@ -290,9 +203,9 @@ struct _SpiNode { SpiResult (*remove_port) (SpiNode *node, uint32_t port_id); - SpiResult (*get_port_formats) (SpiNode *node, + SpiResult (*enum_port_formats) (SpiNode *node, uint32_t port_id, - unsigned int format_idx, + unsigned int index, SpiParams **format); SpiResult (*set_port_format) (SpiNode *node, uint32_t port_id, @@ -323,8 +236,43 @@ struct _SpiNode { unsigned int n_data, SpiDataInfo *data); + + /** + * SpiNode::enum_interface_info: + * @node: a #SpiNode + * @index: the interface index + * @info: result to hold SpiInterfaceInfo. + * + * Get the interface provided by @node at @index. + * + * Returns: #SPI_RESULT_OK on success + * #SPI_RESULT_NOT_IMPLEMENTED when there are no extensions + * #SPI_RESULT_INVALID_ARGUMENTS when node or info is %NULL + * #SPI_RESULT_ENUM_END when there are no more infos + */ + SpiResult (*enum_interface_info) (SpiNode *node, + unsigned int index, + const SpiInterfaceInfo **info); + /** + * SpiNode::enum_interface_info: + * @node: a #SpiNode + * @index: the interface index + * @info: result to hold SpiInterfaceInfo. + * + * Get the interface provided by @node at @index. + * + * Returns: #SPI_RESULT_OK on success + * #SPI_RESULT_NOT_IMPLEMENTED when there are no extensions + * #SPI_RESULT_INVALID_ARGUMENTS when node or info is %NULL + * #SPI_RESULT_ENUM_END when there are no more infos + */ + SpiResult (*get_interface) (SpiNode *node, + uint32_t interface_id, + void **interface); }; -G_END_DECLS +#ifdef __cplusplus +} /* extern "C" */ +#endif #endif /* __SPI_NODE_H__ */ diff --git a/pinos/spi/params.h b/pinos/spi/params.h index 1c6feb0bb..959be89fc 100644 --- a/pinos/spi/params.h +++ b/pinos/spi/params.h @@ -20,12 +20,14 @@ #ifndef __SPI_PARAMS_H__ #define __SPI_PARAMS_H__ -G_BEGIN_DECLS - -#include +#ifdef __cplusplus +extern "C" { +#endif typedef struct _SpiParams SpiParams; +#include + /** * SpiParamType: */ @@ -92,7 +94,7 @@ typedef enum { typedef struct { const char *name; const char *description; - int size; + size_t size; const void *value; } SpiParamRangeInfo; @@ -112,13 +114,13 @@ typedef struct { * @priv: extra private data */ typedef struct { - int id; + uint32_t id; const char *name; const char *description; SpiParamFlags flags; SpiParamType type; - int maxsize; - int default_size; + size_t maxsize; + size_t default_size; const void *default_value; SpiParamRangeType range_type; const SpiParamRangeInfo *range_values; @@ -141,11 +143,11 @@ struct _SpiParams { * Gets the information about the parameter at @idx in @params. * * Returns: #SPI_RESULT_OK on success. - * #SPI_RESULT_NO_MORE_PARAM_INFO when there is no param info + * #SPI_RESULT_ENM_END when there is no param info * at @idx. This can be used to iterate the @params. */ - SpiResult (*get_param_info) (const SpiParams *params, - int idx, + SpiResult (*enum_param_info) (const SpiParams *params, + unsigned int idx, const SpiParamInfo **infos); /** * SpiParams::set_param @@ -164,7 +166,7 @@ struct _SpiParams { * #SPI_RESULT_WRONG_PARAM_TYPE when @type is not correct */ SpiResult (*set_param) (SpiParams *params, - int id, + uint32_t id, SpiParamType type, size_t size, const void *value); @@ -183,12 +185,14 @@ struct _SpiParams { * #SPI_RESULT_PARAM_UNSET when no value has been set yet */ SpiResult (*get_param) (const SpiParams *params, - int id, + uint32_t id, SpiParamType *type, size_t *size, const void **value); }; -G_END_DECLS +#ifdef __cplusplus +} /* extern "C" */ +#endif #endif /* __SPI_PARAMS_H__ */ diff --git a/pinos/spi/port.h b/pinos/spi/port.h new file mode 100644 index 000000000..85a012997 --- /dev/null +++ b/pinos/spi/port.h @@ -0,0 +1,91 @@ +/* Simple Plugin Interface + * Copyright (C) 2016 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 __SPI_PORT_H__ +#define __SPI_PORT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * SpiPortInfoFlags: + * @SPI_PORT_INFO_FLAG_NONE: no flags + * @SPI_PORT_INFO_FLAG_REMOVABLE: port can be removed + * @SPI_PORT_INFO_FLAG_OPTIONAL: processing on port is optional + * @SPI_PORT_INFO_FLAG_CAN_GIVE_BUFFER: the port can give a buffer + * @SPI_PORT_INFO_FLAG_CAN_USE_BUFFER: the port can use a provided buffer + * @SPI_PORT_INFO_FLAG_IN_PLACE: the port can process data in-place and will need + * a writable input buffer when no output buffer is specified. + * @SPI_PORT_INFO_FLAG_NO_REF: the port does not keep a ref on the buffer + */ +typedef enum { + SPI_PORT_INFO_FLAG_NONE = 0, + SPI_PORT_INFO_FLAG_REMOVABLE = 1 << 0, + SPI_PORT_INFO_FLAG_OPTIONAL = 1 << 1, + SPI_PORT_INFO_FLAG_CAN_GIVE_BUFFER = 1 << 2, + SPI_PORT_INFO_FLAG_CAN_USE_BUFFER = 1 << 3, + SPI_PORT_INFO_FLAG_IN_PLACE = 1 << 4, + SPI_PORT_INFO_FLAG_NO_REF = 1 << 5, +} SpiPortInfoFlags; + +/** + * SpiPortInfo + * @flags: extra port flags + * @size: minimum size of the buffers or 0 when not specified + * @align: required alignment of the data + * @maxbuffering: the maximum amount of bytes that the element will keep + * around internally + * @latency: latency on this port in nanoseconds + * @features: NULL terminated array of extra port features + * + */ +typedef struct { + SpiPortInfoFlags flags; + size_t minsize; + uint32_t align; + unsigned int maxbuffering; + uint64_t latency; + const char **features; +} SpiPortInfo; + +/** + * SpiPortStatusFlags: + * @SPI_PORT_STATUS_FLAG_NONE: no status flags + * @SPI_PORT_STATUS_FLAG_HAVE_OUTPUT: port has output + * @SPI_PORT_STATUS_FLAG_NEED_INPUT: port needs input + */ +typedef enum { + SPI_PORT_STATUS_FLAG_NONE = 0, + SPI_PORT_STATUS_FLAG_HAVE_OUTPUT = 1 << 0, + SPI_PORT_STATUS_FLAG_NEED_INPUT = 1 << 1, +} SpiPortStatusFlags; + +typedef struct { + SpiPortStatusFlags flags; +} SpiPortStatus; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /* __SPI_PORT_H__ */ diff --git a/pinos/tests/alsa-utils.c b/pinos/tests/alsa-utils.c new file mode 100644 index 000000000..5c65d1353 --- /dev/null +++ b/pinos/tests/alsa-utils.c @@ -0,0 +1,448 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static int verbose = 0; /* verbose flag */ + +#if 0 +static void +generate_sine(const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + int count, double *_phase) +{ + static double max_phase = 2. * M_PI; + double phase = *_phase; + double step = max_phase*freq/(double)rate; + unsigned char *samples[channels]; + int steps[channels]; + unsigned int chn; + int format_bits = snd_pcm_format_width(format); + unsigned int maxval = (1 << (format_bits - 1)) - 1; + int bps = format_bits / 8; /* bytes per sample */ + int phys_bps = snd_pcm_format_physical_width(format) / 8; + int big_endian = snd_pcm_format_big_endian(format) == 1; + int to_unsigned = snd_pcm_format_unsigned(format) == 1; + int is_float = (format == SND_PCM_FORMAT_FLOAT_LE || + format == SND_PCM_FORMAT_FLOAT_BE); + + /* verify and prepare the contents of areas */ + for (chn = 0; chn < channels; chn++) { + if ((areas[chn].first % 8) != 0) { + printf("areas[%i].first == %i, aborting...\n", chn, areas[chn].first); + exit(EXIT_FAILURE); + } + samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8)); + if ((areas[chn].step % 16) != 0) { + printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step); + exit(EXIT_FAILURE); + } + steps[chn] = areas[chn].step / 8; + samples[chn] += offset * steps[chn]; + } + /* fill the channel areas */ + while (count-- > 0) { + union { + float f; + int i; + } fval; + int res, i; + if (is_float) { + fval.f = sin(phase) * maxval; + res = fval.i; + } else + res = sin(phase) * maxval; + if (to_unsigned) + res ^= 1U << (format_bits - 1); + for (chn = 0; chn < channels; chn++) { + /* Generate data in native endian format */ + if (big_endian) { + for (i = 0; i < bps; i++) + *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff; + } else { + for (i = 0; i < bps; i++) + *(samples[chn] + i) = (res >> i * 8) & 0xff; + } + samples[chn] += steps[chn]; + } + phase += step; + if (phase >= max_phase) + phase -= max_phase; + } + *_phase = phase; +} +#endif + +#define CHECK(s,msg) if ((err = (s)) < 0) { printf (msg ": %s\n", snd_strerror(err)); return err; } + +static snd_pcm_format_t +spi_alsa_format_to_alsa (const char *format) +{ + if (strcmp (format, "S8") == 0) + return SND_PCM_FORMAT_S8; + if (strcmp (format, "U8") == 0) + return SND_PCM_FORMAT_U8; + /* 16 bit */ + if (strcmp (format, "S16LE") == 0) + return SND_PCM_FORMAT_S16_LE; + if (strcmp (format, "S16BE") == 0) + return SND_PCM_FORMAT_S16_BE; + if (strcmp (format, "U16LE") == 0) + return SND_PCM_FORMAT_U16_LE; + if (strcmp (format, "U16BE") == 0) + return SND_PCM_FORMAT_U16_BE; + /* 24 bit in low 3 bytes of 32 bits */ + if (strcmp (format, "S24_32LE") == 0) + return SND_PCM_FORMAT_S24_LE; + if (strcmp (format, "S24_32BE") == 0) + return SND_PCM_FORMAT_S24_BE; + if (strcmp (format, "U24_32LE") == 0) + return SND_PCM_FORMAT_U24_LE; + if (strcmp (format, "U24_32BE") == 0) + return SND_PCM_FORMAT_U24_BE; + /* 24 bit in 3 bytes */ + if (strcmp (format, "S24LE") == 0) + return SND_PCM_FORMAT_S24_3LE; + if (strcmp (format, "S24BE") == 0) + return SND_PCM_FORMAT_S24_3BE; + if (strcmp (format, "U24LE") == 0) + return SND_PCM_FORMAT_U24_3LE; + if (strcmp (format, "U24BE") == 0) + return SND_PCM_FORMAT_U24_3BE; + /* 32 bit */ + if (strcmp (format, "S32LE") == 0) + return SND_PCM_FORMAT_S32_LE; + if (strcmp (format, "S32BE") == 0) + return SND_PCM_FORMAT_S32_BE; + if (strcmp (format, "U32LE") == 0) + return SND_PCM_FORMAT_U32_LE; + if (strcmp (format, "U32BE") == 0) + return SND_PCM_FORMAT_U32_BE; + + return SND_PCM_FORMAT_UNKNOWN; +} + +static int +set_hwparams (SpiALSASink *this) +{ + unsigned int rrate; + snd_pcm_uframes_t size; + int err, dir; + snd_pcm_hw_params_t *params; + snd_pcm_format_t format; + SpiALSAState *state = &this->state; + SpiALSASinkFormat *fmt = &this->current_format; + snd_pcm_t *handle = state->handle; + unsigned int buffer_time; + unsigned int period_time; + + snd_pcm_hw_params_alloca (¶ms); + /* choose all parameters */ + CHECK (snd_pcm_hw_params_any (handle, 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"); + /* set the interleaved read/write format */ + CHECK (snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_MMAP_INTERLEAVED), "set_access"); + + /* set the sample format */ + format = spi_alsa_format_to_alsa (fmt->format); + printf ("Stream parameters are %iHz, %s, %i channels\n", fmt->samplerate, snd_pcm_format_name(format), fmt->channels); + CHECK (snd_pcm_hw_params_set_format (handle, params, format), "set_format"); + /* set the count of channels */ + CHECK (snd_pcm_hw_params_set_channels (handle, params, fmt->channels), "set_channels"); + /* set the stream rate */ + rrate = fmt->samplerate; + CHECK (snd_pcm_hw_params_set_rate_near (handle, params, &rrate, 0), "set_rate_near"); + if (rrate != fmt->samplerate) { + printf("Rate doesn't match (requested %iHz, get %iHz)\n", fmt->samplerate, rrate); + return -EINVAL; + } + /* set the buffer time */ + buffer_time = this->params.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_get_buffer_size (params, &size), "get_buffer_size"); + state->buffer_size = size; + + /* set the period time */ + period_time = this->params.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_get_period_size (params, &size, &dir), "get_period_size"); + state->period_size = size; + + /* write the parameters to device */ + CHECK (snd_pcm_hw_params (handle, params), "set_hw_params"); + + return 0; +} + +static int +set_swparams (SpiALSASink *this) +{ + SpiALSAState *state = &this->state; + snd_pcm_t *handle = state->handle; + int err = 0; + snd_pcm_sw_params_t *params; + + snd_pcm_sw_params_alloca (¶ms); + + /* get the current params */ + CHECK (snd_pcm_sw_params_current (handle, 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"); + + /* 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, + this->params.period_event ? state->buffer_size : state->period_size), "set_avail_min"); + /* enable period events when requested */ + if (this->params.period_event) { + CHECK (snd_pcm_sw_params_set_period_event (handle, params, 1), "set_period_event"); + } + /* write the parameters to the playback device */ + CHECK (snd_pcm_sw_params (handle, params), "sw_params"); + + return 0; +} + +/* + * Underrun and suspend recovery + */ +static int +xrun_recovery (snd_pcm_t *handle, int err) +{ + if (verbose) + printf("stream recovery\n"); + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare(handle); + 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) + sleep(1); /* wait until the suspend flag is released */ + if (err < 0) { + err = snd_pcm_prepare(handle); + if (err < 0) + printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); + } + return 0; + } + return err; +} + +#if 0 +/* + * Transfer method - write and wait for room in buffer using poll + */ +static int +wait_for_poll (snd_pcm_t *handle, struct pollfd *ufds, unsigned int count) +{ + unsigned short revents; + + while (1) { + poll(ufds, count, -1); + snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents); + if (revents & POLLERR) + return -EIO; + if (revents & POLLOUT) + return 0; + } +} +#endif + +/* + * Transfer method - direct write only + */ +static void * +direct_loop (void *user_data) +{ + SpiALSASink *this = user_data; + SpiALSAState *state = &this->state; + snd_pcm_t *handle = state->handle; + const snd_pcm_channel_area_t *my_areas; + snd_pcm_uframes_t offset, frames, size; + snd_pcm_sframes_t avail, commitres; + snd_pcm_state_t st; + int err, first = 1; + + while (state->running) { + st = snd_pcm_state(handle); + if (st == SND_PCM_STATE_XRUN) { + err = xrun_recovery(handle, -EPIPE); + if (err < 0) { + printf("XRUN recovery failed: %s\n", snd_strerror(err)); + return NULL; + } + first = 1; + } else if (st == SND_PCM_STATE_SUSPENDED) { + err = xrun_recovery(handle, -ESTRPIPE); + if (err < 0) { + printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); + return NULL; + } + } + avail = snd_pcm_avail_update(handle); + if (avail < 0) { + err = xrun_recovery(handle, avail); + if (err < 0) { + printf("avail update failed: %s\n", snd_strerror(err)); + return NULL; + } + first = 1; + continue; + } + if (avail < state->period_size) { + if (first) { + first = 0; + err = snd_pcm_start(handle); + if (err < 0) { + printf("Start error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + } else { + err = snd_pcm_wait(handle, -1); + if (err < 0) { + if ((err = xrun_recovery(handle, err)) < 0) { + printf("snd_pcm_wait error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + first = 1; + } + } + continue; + } + size = state->period_size; + while (size > 0) { + frames = size; + err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); + if (err < 0) { + if ((err = xrun_recovery(handle, err)) < 0) { + printf("MMAP begin avail error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + first = 1; + } + + { + SpiEvent event; + ALSABuffer *buffer = &this->buffer; + + event.refcount = 1; + event.notify = NULL; + event.type = SPI_EVENT_TYPE_REQUEST_DATA; + event.port_id = 0; + event.data = buffer; + + buffer->buffer.refcount = 1; + buffer->buffer.notify = NULL; + buffer->buffer.size = sizeof (ALSABuffer); + buffer->buffer.n_metas = 1; + buffer->buffer.metas = buffer->meta; + buffer->buffer.n_datas = 1; + buffer->buffer.datas = buffer->data; + + buffer->header.flags = 0; + buffer->header.seq = 0; + buffer->header.pts = 0; + buffer->header.dts_offset = 0; + + buffer->meta[0].type = SPI_META_TYPE_HEADER; + buffer->meta[0].data = &buffer->header; + buffer->meta[0].size = sizeof (buffer->header); + + buffer->data[0].type = SPI_DATA_TYPE_MEMPTR; + buffer->data[0].data = (uint8_t *)my_areas[0].addr + (offset * sizeof (uint16_t) * 2); + buffer->data[0].size = frames * sizeof (uint16_t) * 2; + + printf ("fill data\n"); + this->event_cb (&this->node, &event,this->user_data); + + spi_buffer_unref ((SpiBuffer *)event.data); + } + if (this->input_buffer) { + if (this->input_buffer != &this->buffer.buffer) { + printf ("copy input !\n"); + } + spi_buffer_unref (this->input_buffer); + this->input_buffer = NULL; + } + + commitres = snd_pcm_mmap_commit(handle, offset, frames); + if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { + if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { + printf("MMAP commit error: %s\n", snd_strerror(err)); + exit(EXIT_FAILURE); + } + first = 1; + } + size -= frames; + } + } + return NULL; +} + +static int +spi_alsa_open (SpiALSASink *this) +{ + SpiALSAState *state = &this->state; + int err; + + CHECK (snd_output_stdio_attach (&state->output, stdout, 0), "attach failed"); + + printf ("Playback device is '%s'\n", this->params.device); + CHECK (snd_pcm_open (&state->handle, + this->params.device, + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK | + SND_PCM_NO_AUTO_RESAMPLE | + SND_PCM_NO_AUTO_CHANNELS | + SND_PCM_NO_AUTO_FORMAT), "open failed"); + + return 0; +} + +static int +spi_alsa_start (SpiALSASink *this) +{ + SpiALSAState *state = &this->state; + int err; + + CHECK (set_hwparams (this), "hwparams"); + CHECK (set_swparams (this), "swparams"); + + state->running = true; + if ((err = pthread_create (&state->thread, NULL, direct_loop, this)) != 0) { + printf ("can't create thread: %d", err); + state->running = false; + } + return err; +} + +static int +spi_alsa_stop (SpiALSASink *this) +{ + SpiALSAState *state = &this->state; + + if (state->running) { + state->running = false; + pthread_join (state->thread, NULL); + } + return 0; +} + +static int +spi_alsa_close (SpiALSASink *this) +{ + SpiALSAState *state = &this->state; + int err = 0; + + CHECK (snd_pcm_close (state->handle), "close failed"); + + return err; +} diff --git a/pinos/tests/spi-alsa-sink.c b/pinos/tests/spi-alsa-sink.c new file mode 100644 index 000000000..9fe0b5d4c --- /dev/null +++ b/pinos/tests/spi-alsa-sink.c @@ -0,0 +1,953 @@ +/* Spi ALSA Sink + * Copyright (C) 2016 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 "spi-plugins.h" + +typedef struct _SpiALSASink SpiALSASink; + + +static const char default_device[] = "default"; +static const uint32_t default_buffer_time = 500000; +static const uint32_t default_period_time = 100000; +static const bool default_period_event = 0; + +typedef struct { + SpiParams param; + char device[64]; + char device_name[128]; + char card_name[128]; + uint32_t buffer_time; + uint32_t period_time; + bool period_event; +} SpiALSASinkParams; + +static void +reset_alsa_sink_params (SpiALSASinkParams *params) +{ + strncpy (params->device, default_device, 64); + params->buffer_time = default_buffer_time; + params->period_time = default_period_time; + params->period_event = default_period_event; +} + +typedef struct { + SpiParams param; + char media_type[32]; + uint32_t unset_mask; + char format[16]; + uint32_t layout; + uint32_t samplerate; + uint32_t channels; + uint32_t position[16]; + uint32_t mpegversion; + uint32_t mpegaudioversion; + bool parsed; +} SpiALSASinkFormat; + +typedef struct { + 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]; + pthread_t thread; + bool running; +} SpiALSAState; + + +typedef struct _ALSABuffer ALSABuffer; + +struct _ALSABuffer { + SpiBuffer buffer; + SpiMeta meta[1]; + SpiMetaHeader header; + SpiData data[1]; + ALSABuffer *next; +}; + +struct _SpiALSASink { + SpiNode node; + + SpiALSASinkParams params; + + bool activated; + + SpiEvent *event; + SpiEvent last_event; + + SpiEventCallback event_cb; + void *user_data; + + int have_format; + SpiALSASinkFormat current_format; + + SpiALSAState state; + + SpiBuffer *input_buffer; + + ALSABuffer buffer; +}; + +#include "alsa-utils.c" + +static const uint32_t default_samplerate = 44100; +static const uint32_t min_samplerate = 1; +static const uint32_t max_samplerate = UINT32_MAX; + +static const SpiParamRangeInfo int32_range[] = { + { "min", "Minimum value", 4, &min_samplerate }, + { "max", "Maximum value", 4, &max_samplerate }, + { NULL, NULL, 0, NULL } +}; + +enum { + PARAM_ID_DEVICE, + PARAM_ID_DEVICE_NAME, + PARAM_ID_CARD_NAME, + PARAM_ID_BUFFER_TIME, + PARAM_ID_PERIOD_TIME, + PARAM_ID_PERIOD_EVENT, + PARAM_ID_LAST, +}; + +static const SpiParamInfo param_info[] = +{ + { PARAM_ID_DEVICE, "device", "ALSA device, as defined in an asound configuration file", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_STRING, 63, + strlen (default_device)+1, default_device, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { PARAM_ID_DEVICE_NAME, "device-name", "Human-readable name of the sound device", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_STRING, 127, + 0, NULL, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { PARAM_ID_CARD_NAME, "card-name", "Human-readable name of the sound card", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_STRING, 127, + 0, NULL, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { PARAM_ID_BUFFER_TIME, "buffer-time", "The total size of the buffer in time", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_buffer_time, + SPI_PARAM_RANGE_TYPE_MIN_MAX, int32_range, + NULL, + NULL }, + { PARAM_ID_PERIOD_TIME, "period-time", "The size of a period in time", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_period_time, + SPI_PARAM_RANGE_TYPE_MIN_MAX, int32_range, + NULL, + NULL }, + { PARAM_ID_PERIOD_EVENT, "period-event", "Generate an event each period", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_BOOL, sizeof (bool), + sizeof (bool), &default_period_event, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, +}; + +#define CHECK_TYPE(type,expected) if (type != expected) return SPI_RESULT_WRONG_PARAM_TYPE; +#define CHECK_SIZE(size,expected) if (size != expected) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_SIZE_RANGE(size,minsize,maxsize) if (size > maxsize || size < minsize) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_SIZE_MAX(size,maxsize) if (size > maxsize) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_UNSET(mask,index) if (mask & (1 << index)) return SPI_RESULT_PARAM_UNSET; + +static SpiResult +enum_param_info (const SpiParams *params, + unsigned int index, + const SpiParamInfo **info) +{ + if (index >= PARAM_ID_LAST) + return SPI_RESULT_ENUM_END; + *info = ¶m_info[index]; + return SPI_RESULT_OK; +} + +static SpiResult +set_param (SpiParams *params, + uint32_t id, + SpiParamType type, + size_t size, + const void *value) +{ + SpiResult res = SPI_RESULT_OK; + SpiALSASinkParams *p = (SpiALSASinkParams *) params; + + switch (id) { + case PARAM_ID_DEVICE: + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 64); + strncpy (p->device, value, 64); + break; + case PARAM_ID_BUFFER_TIME: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&p->buffer_time, value, size); + break; + case PARAM_ID_PERIOD_TIME: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&p->period_time, value, size); + break; + case PARAM_ID_PERIOD_EVENT: + CHECK_TYPE (type, SPI_PARAM_TYPE_BOOL); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&p->period_event, value, size); + break; + default: + res = SPI_RESULT_INVALID_PARAM_ID; + break; + } + return res; +} + +static SpiResult +get_param (const SpiParams *params, + uint32_t id, + SpiParamType *type, + size_t *size, + const void **value) +{ + SpiResult res = SPI_RESULT_OK; + SpiALSASinkParams *p = (SpiALSASinkParams *) params; + + switch (id) { + case PARAM_ID_DEVICE: + *type = SPI_PARAM_TYPE_STRING; + *value = p->device; + *size = strlen (p->device)+1; + break; + case PARAM_ID_DEVICE_NAME: + *type = SPI_PARAM_TYPE_STRING; + *value = p->device_name; + *size = strlen (p->device_name)+1; + break; + case PARAM_ID_CARD_NAME: + *type = SPI_PARAM_TYPE_STRING; + *value = p->card_name; + *size = strlen (p->card_name)+1; + break; + case PARAM_ID_BUFFER_TIME: + *type = SPI_PARAM_TYPE_UINT32; + *value = &p->buffer_time; + *size = sizeof (uint32_t); + break; + case PARAM_ID_PERIOD_TIME: + *type = SPI_PARAM_TYPE_UINT32; + *value = &p->period_time; + *size = sizeof (uint32_t); + break; + case PARAM_ID_PERIOD_EVENT: + *type = SPI_PARAM_TYPE_BOOL; + *value = &p->period_event; + *size = sizeof (bool); + break; + default: + res = SPI_RESULT_INVALID_PARAM_ID; + break; + } + return res; +} + + +static SpiResult +spi_alsa_sink_node_get_params (SpiNode *node, + SpiParams **params) +{ + static SpiALSASinkParams p; + SpiALSASink *this = (SpiALSASink *) node; + + memcpy (&p, &this->params, sizeof (p)); + *params = &p.param; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_set_params (SpiNode *node, + const SpiParams *params) +{ + SpiALSASink *this = (SpiALSASink *) node; + SpiALSASinkParams *p = &this->params; + SpiParamType type; + size_t size; + const void *value; + + if (params == NULL) { + reset_alsa_sink_params (p); + return SPI_RESULT_OK; + } + + if (params->get_param (params, PARAM_ID_DEVICE, &type, &size, &value) == 0) { + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 64); + strncpy (p->device, value, 64); + } + if (params->get_param (params, PARAM_ID_BUFFER_TIME, &type, &size, &value) == 0) { + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&p->buffer_time, value, size); + } + if (params->get_param (params, PARAM_ID_PERIOD_TIME, &type, &size, &value) == 0) { + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&p->period_time, value, size); + } + if (params->get_param (params, PARAM_ID_PERIOD_EVENT, &type, &size, &value) == 0) { + CHECK_TYPE (type, SPI_PARAM_TYPE_BOOL); + CHECK_SIZE (size, sizeof (bool)); + memcpy (&p->period_event, value, size); + } + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_send_command (SpiNode *node, + SpiCommand *command) +{ + SpiALSASink *this = (SpiALSASink *) node; + SpiResult res = SPI_RESULT_NOT_IMPLEMENTED; + + switch (command->type) { + case SPI_COMMAND_INVALID: + res = SPI_RESULT_INVALID_COMMAND; + break; + case SPI_COMMAND_ACTIVATE: + if (!this->activated) { + spi_alsa_open (this); + this->activated = true; + } + this->last_event.type = SPI_EVENT_TYPE_ACTIVATED; + this->last_event.data = NULL; + this->last_event.size = 0; + this->event = &this->last_event; + res = SPI_RESULT_HAVE_EVENT; + break; + case SPI_COMMAND_DEACTIVATE: + if (this->activated) { + spi_alsa_close (this); + this->activated = false; + } + this->last_event.type = SPI_EVENT_TYPE_DEACTIVATED; + this->last_event.data = NULL; + this->last_event.size = 0; + this->event = &this->last_event; + res = SPI_RESULT_HAVE_EVENT; + break; + case SPI_COMMAND_START: + spi_alsa_start (this); + res = SPI_RESULT_OK; + break; + case SPI_COMMAND_STOP: + spi_alsa_stop (this); + res = SPI_RESULT_OK; + break; + case SPI_COMMAND_FLUSH: + break; + case SPI_COMMAND_DRAIN: + break; + case SPI_COMMAND_MARKER: + break; + } + return res; +} + +static SpiResult +spi_alsa_sink_node_get_event (SpiNode *node, + SpiEvent **event) +{ + SpiALSASink *this = (SpiALSASink *) node; + + if (this->event == NULL) + return SPI_RESULT_ERROR; + + *event = this->event; + this->event = NULL; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_set_event_callback (SpiNode *node, + SpiEventCallback event, + void *user_data) +{ + SpiALSASink *this = (SpiALSASink *) node; + + this->event_cb = event; + this->user_data = user_data; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_get_n_ports (SpiNode *node, + unsigned int *n_input_ports, + unsigned int *max_input_ports, + unsigned int *n_output_ports, + unsigned int *max_output_ports) +{ + *n_input_ports = 1; + *n_output_ports = 0; + *max_input_ports = 1; + *max_output_ports = 0; + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_get_port_ids (SpiNode *node, + unsigned int n_input_ports, + uint32_t *input_ids, + unsigned int n_output_ports, + uint32_t *output_ids) +{ + if (n_input_ports > 0) + input_ids[0] = 0; + + return SPI_RESULT_OK; +} + + +static SpiResult +spi_alsa_sink_node_add_port (SpiNode *node, + SpiDirection direction, + uint32_t *port_id) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_alsa_sink_node_remove_port (SpiNode *node, + uint32_t port_id) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static const SpiParamRangeInfo format_format_range[] = { + { "S8", "S8", 2, "S8" }, + { "U8", "U8", 2, "U8" }, + { "S16LE", "S16LE", 5, "S16LE" }, + { "S16BE", "S16BE", 5, "S16BE" }, + { "U16LE", "U16LE", 5, "U16LE" }, + { "U16BE", "U16BE", 5, "U16BE" }, + { "S24_32LE", "S24_32LE", 8, "S24_32LE" }, + { "S24_32BE", "S24_32BE", 8, "S24_32BE" }, + { "U24_32LE", "U24_32LE", 8, "U24_32LE" }, + { "U24_32BE", "U24_32BE", 8, "U24_32BE" }, + { "S32LE", "S32LE", 5, "S32LE" }, + { "S32BE", "S32BE", 5, "S32BE" }, + { "U32LE", "U32LE", 5, "U32LE" }, + { "U32BE", "U32BE", 5, "U32BE" }, + { "S24LE", "S24LE", 5, "S24LE" }, + { "S24BE", "S24BE", 5, "S24BE" }, + { "U24LE", "U24LE", 5, "U24LE" }, + { "U24BE", "U24BE", 5, "U24BE" }, + { "S20LE", "S20LE", 5, "S20LE" }, + { "S20BE", "S20BE", 5, "S20BE" }, + { "U20LE", "U20LE", 5, "U20LE" }, + { "U20BE", "U20BE", 5, "U20BE" }, + { "S18LE", "S18LE", 5, "S18LE" }, + { "S18BE", "S18BE", 5, "S18BE" }, + { "U18LE", "U18LE", 5, "U18LE" }, + { "U18BE", "U18BE", 5, "U18BE" }, + { "F32LE", "F32LE", 5, "F32LE" }, + { "F32BE", "F32BE", 5, "F32BE" }, + { "F64LE", "F64LE", 5, "F64LE" }, + { "F64BE", "F64BE", 5, "F64BE" }, + { NULL, NULL, 0, NULL } +}; + +enum { + SPI_PARAM_ID_MEDIA_TYPE, + SPI_PARAM_ID_FORMAT, + SPI_PARAM_ID_LAYOUT, + SPI_PARAM_ID_SAMPLERATE, + SPI_PARAM_ID_CHANNELS, + SPI_PARAM_ID_MPEG_VERSION, + SPI_PARAM_ID_MPEG_AUDIO_VERSION, + SPI_PARAM_ID_PARSED, +}; + +static const int32_t format_default_layout = 1; + +static const SpiParamInfo raw_format_param_info[] = +{ + { SPI_PARAM_ID_MEDIA_TYPE, "media-type", "The media type", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_STRING, 32, + strlen ("audio/x-raw")+1, "audio/x-raw", + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { SPI_PARAM_ID_FORMAT, "format", "The media format", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_STRING, 16, + 0, NULL, + SPI_PARAM_RANGE_TYPE_ENUM, format_format_range, + NULL, + NULL }, + { SPI_PARAM_ID_LAYOUT, "layout", "Sample Layout", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &format_default_layout, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { SPI_PARAM_ID_SAMPLERATE, "rate", "Audio sample rate", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + 0, NULL, + SPI_PARAM_RANGE_TYPE_MIN_MAX, int32_range, + NULL, + NULL }, + { SPI_PARAM_ID_CHANNELS, "channels", "Audio channels", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + 0, NULL, + SPI_PARAM_RANGE_TYPE_MIN_MAX, int32_range, + NULL, + NULL }, +}; + +static SpiResult +enum_raw_format_param_info (const SpiParams *params, + unsigned int index, + const SpiParamInfo **info) +{ + if (index >= 5) + return SPI_RESULT_ENUM_END; + *info = &raw_format_param_info[index]; + return SPI_RESULT_OK; +} + +static const uint32_t default_mpeg_version = 1; +static const uint32_t min_mpeg_audio_version = 1; +static const uint32_t max_mpeg_audio_version = 2; +static const bool default_parsed = 1; + +static const SpiParamRangeInfo mpeg_audio_version_range[] = { + { "min", "Minimum value", 4, &min_mpeg_audio_version }, + { "max", "Maximum value", 4, &max_mpeg_audio_version }, + { NULL, NULL, 0, NULL } +}; + +static const SpiParamInfo mpeg_format_param_info[] = +{ + { SPI_PARAM_ID_MEDIA_TYPE, "media-type", "The media type", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_STRING, 32, + strlen ("audio/mpeg")+1, "audio/mpeg", + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { SPI_PARAM_ID_MPEG_VERSION, "mpegversion", "The MPEG version", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_mpeg_version, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { SPI_PARAM_ID_MPEG_AUDIO_VERSION, "mpegaudioversion", "The MPEG audio version", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + 0, NULL, + SPI_PARAM_RANGE_TYPE_MIN_MAX, mpeg_audio_version_range, + NULL, + NULL }, + { SPI_PARAM_ID_PARSED, "parsed", "Parsed input", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_BOOL, sizeof (bool), + sizeof (bool), &default_parsed, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, +}; + +static SpiResult +enum_mpeg_format_param_info (const SpiParams *params, + unsigned int index, + const SpiParamInfo **info) +{ + if (index >= 4) + return SPI_RESULT_ENUM_END; + *info = &mpeg_format_param_info[index]; + return SPI_RESULT_OK; +} + + +#define CHECK_TYPE(type,expected) if (type != expected) return SPI_RESULT_WRONG_PARAM_TYPE; +#define MARK_SET(mask,index) (mask &= ~(1 << index)) + +static SpiResult +set_format_param (SpiParams *params, + uint32_t id, + SpiParamType type, + size_t size, + const void *value) +{ + SpiALSASinkFormat *f = (SpiALSASinkFormat *) params; + + switch (id) { + case SPI_PARAM_ID_FORMAT: + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 16); + strncpy (f->format, value, 16); + MARK_SET (f->unset_mask, 1); + break; + case SPI_PARAM_ID_LAYOUT: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->layout, value, size); + MARK_SET (f->unset_mask, 2); + break; + case SPI_PARAM_ID_SAMPLERATE: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->samplerate, value, size); + MARK_SET (f->unset_mask, 3); + break; + case SPI_PARAM_ID_CHANNELS: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->channels, value, size); + MARK_SET (f->unset_mask, 4); + break; + case SPI_PARAM_ID_MPEG_AUDIO_VERSION: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->mpegaudioversion, value, size); + MARK_SET (f->unset_mask, 6); + break; + default: + return SPI_RESULT_INVALID_PARAM_ID; + } + + return SPI_RESULT_OK; +} + +static SpiResult +get_format_param (const SpiParams *params, + uint32_t id, + SpiParamType *type, + size_t *size, + const void **value) +{ + SpiALSASinkFormat *f = (SpiALSASinkFormat *) params; + + switch (id) { + case SPI_PARAM_ID_MEDIA_TYPE: + CHECK_UNSET (f->unset_mask, 0); + *type = SPI_PARAM_TYPE_STRING; + *value = f->media_type; + *size = strlen (f->media_type)+1; + break; + case SPI_PARAM_ID_FORMAT: + CHECK_UNSET (f->unset_mask, 1); + *type = SPI_PARAM_TYPE_STRING; + *value = f->format; + *size = strlen (f->format)+1; + break; + case SPI_PARAM_ID_LAYOUT: + CHECK_UNSET (f->unset_mask, 2); + *type = SPI_PARAM_TYPE_UINT32; + *value = &f->layout; + *size = sizeof (uint32_t); + break; + case SPI_PARAM_ID_SAMPLERATE: + CHECK_UNSET (f->unset_mask, 3); + *type = SPI_PARAM_TYPE_UINT32; + *value = &f->samplerate; + *size = sizeof (uint32_t); + break; + case SPI_PARAM_ID_CHANNELS: + CHECK_UNSET (f->unset_mask, 4); + *type = SPI_PARAM_TYPE_UINT32; + *value = &f->channels; + *size = sizeof (uint32_t); + break; + case SPI_PARAM_ID_MPEG_VERSION: + CHECK_UNSET (f->unset_mask, 5); + *type = SPI_PARAM_TYPE_UINT32; + *value = &f->mpegversion; + *size = sizeof (uint32_t); + break; + case SPI_PARAM_ID_MPEG_AUDIO_VERSION: + CHECK_UNSET (f->unset_mask, 6); + *type = SPI_PARAM_TYPE_UINT32; + *value = &f->mpegaudioversion; + *size = sizeof (uint32_t); + break; + case SPI_PARAM_ID_PARSED: + CHECK_UNSET (f->unset_mask, 7); + *type = SPI_PARAM_TYPE_BOOL; + *value = &f->parsed; + *size = sizeof (bool); + break; + default: + return SPI_RESULT_INVALID_PARAM_ID; + } + return SPI_RESULT_OK; +} + + +static SpiResult +spi_alsa_sink_node_enum_port_formats (SpiNode *node, + uint32_t port_id, + unsigned int index, + SpiParams **format) +{ + static SpiALSASinkFormat fmt; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + switch (index) { + case 0: + strcpy (fmt.media_type, "audio/x-raw"); + fmt.unset_mask = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); + fmt.param.enum_param_info = enum_raw_format_param_info; + fmt.param.set_param = set_format_param; + fmt.param.get_param = get_format_param; + break; + case 1: + strcpy (fmt.media_type, "audio/mpeg"); + fmt.mpegversion = 1; + fmt.parsed = 1; + fmt.unset_mask = (1 << 6); + fmt.param.enum_param_info = enum_mpeg_format_param_info; + fmt.param.set_param = set_format_param; + fmt.param.get_param = get_format_param; + break; + default: + return SPI_RESULT_ENUM_END; + } + *format = &fmt.param; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_set_port_format (SpiNode *node, + uint32_t port_id, + int test_only, + const SpiParams *format) +{ + SpiALSASink *this = (SpiALSASink *) node; + SpiParamType type; + size_t size; + const void *value; + SpiALSASinkFormat *fmt = &this->current_format; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + if (format == NULL) { + fmt->param.get_param = NULL; + this->have_format = 0; + return SPI_RESULT_OK; + } + + if (format->get_param (format, + SPI_PARAM_ID_MEDIA_TYPE, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_MEDIA_TYPE; + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 32); + strncpy (fmt->media_type, value, 32); + + if (format->get_param (format, + SPI_PARAM_ID_FORMAT, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_FORMAT_PARAMS; + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 16); + strncpy (fmt->format, value, 16); + + if (format->get_param (format, + SPI_PARAM_ID_LAYOUT, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_FORMAT_PARAMS; + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->layout, value, size); + + if (format->get_param (format, + SPI_PARAM_ID_SAMPLERATE, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_FORMAT_PARAMS; + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->samplerate, value, size); + + if (format->get_param (format, + SPI_PARAM_ID_CHANNELS, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_FORMAT_PARAMS; + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->channels, value, size); + + fmt->param.enum_param_info = enum_raw_format_param_info; + fmt->param.set_param = NULL; + fmt->param.get_param = get_format_param; + this->have_format = 1; + + + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_get_port_format (SpiNode *node, + uint32_t port_id, + const SpiParams **format) +{ + SpiALSASink *this = (SpiALSASink *) node; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + if (!this->have_format) + return SPI_RESULT_NO_FORMAT; + + *format = &this->current_format.param; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_get_port_info (SpiNode *node, + uint32_t port_id, + SpiPortInfo *info) +{ + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + info->flags = SPI_PORT_INFO_FLAG_NONE; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_get_port_params (SpiNode *node, + uint32_t port_id, + SpiParams **params) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_alsa_sink_node_set_port_params (SpiNode *node, + uint32_t port_id, + const SpiParams *params) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_alsa_sink_node_get_port_status (SpiNode *node, + uint32_t port_id, + SpiPortStatus *status) +{ + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + status->flags = SPI_PORT_STATUS_FLAG_NEED_INPUT; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_send_port_data (SpiNode *node, + SpiDataInfo *data) +{ + SpiALSASink *this = (SpiALSASink *) node; + + if (data->port_id != 0) + return SPI_RESULT_INVALID_PORT; + + if (data->buffer != NULL) { + if (!this->have_format) + return SPI_RESULT_NO_FORMAT; + + if (this->input_buffer != NULL) + return SPI_RESULT_HAVE_ENOUGH_INPUT; + + this->input_buffer = spi_buffer_ref (data->buffer); + } + return SPI_RESULT_OK; +} + +static SpiResult +spi_alsa_sink_node_receive_port_data (SpiNode *node, + unsigned int n_data, + SpiDataInfo *data) +{ + return SPI_RESULT_INVALID_PORT; +} + +SpiNode * +spi_alsa_sink_new (void) +{ + SpiNode *node; + SpiALSASink *this; + + node = calloc (1, sizeof (SpiALSASink)); + + node->get_params = spi_alsa_sink_node_get_params; + node->set_params = spi_alsa_sink_node_set_params; + node->send_command = spi_alsa_sink_node_send_command; + node->get_event = spi_alsa_sink_node_get_event; + node->set_event_callback = spi_alsa_sink_node_set_event_callback; + node->get_n_ports = spi_alsa_sink_node_get_n_ports; + node->get_port_ids = spi_alsa_sink_node_get_port_ids; + node->add_port = spi_alsa_sink_node_add_port; + node->remove_port = spi_alsa_sink_node_remove_port; + node->enum_port_formats = spi_alsa_sink_node_enum_port_formats; + node->set_port_format = spi_alsa_sink_node_set_port_format; + node->get_port_format = spi_alsa_sink_node_get_port_format; + node->get_port_info = spi_alsa_sink_node_get_port_info; + node->get_port_params = spi_alsa_sink_node_get_port_params; + node->set_port_params = spi_alsa_sink_node_set_port_params; + node->get_port_status = spi_alsa_sink_node_get_port_status; + node->send_port_data = spi_alsa_sink_node_send_port_data; + node->receive_port_data = spi_alsa_sink_node_receive_port_data; + + this = (SpiALSASink *) node; + this->params.param.enum_param_info = enum_param_info; + this->params.param.set_param = set_param; + this->params.param.get_param = get_param; + reset_alsa_sink_params (&this->params); + + return node; +} diff --git a/pinos/tests/spi-audiotestsrc.c b/pinos/tests/spi-audiotestsrc.c new file mode 100644 index 000000000..2fd1c1da7 --- /dev/null +++ b/pinos/tests/spi-audiotestsrc.c @@ -0,0 +1,863 @@ +/* Spi + * Copyright (C) 2016 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 "spi-plugins.h" + +typedef struct _SpiAudioTestSrc SpiAudioTestSrc; + +typedef struct { + SpiParams param; + uint32_t wave; + double freq; + double volume; +} SpiAudioTestSrcParams; + +typedef struct { + SpiParams param; + char media_type[32]; + uint32_t unset_mask; + char format[16]; + int32_t layout; + int32_t samplerate; + int32_t channels; + int32_t position[16]; +} SpiAudioTestSrcFormat; + +struct _SpiAudioTestSrc { + SpiNode node; + + SpiAudioTestSrcParams params; + SpiAudioTestSrcParams tmp_params; + + SpiEvent *event; + SpiEvent last_event; + + SpiEventCallback event_cb; + void *user_data; + + bool have_format; + SpiAudioTestSrcFormat current_format; + + bool have_input; + SpiBuffer *input_buffer; + + SpiData data; +}; + +static const uint32_t default_wave = 1.0; +static const double default_volume = 1.0; +static const double min_volume = 0.0; +static const double max_volume = 10.0; +static const double default_freq = 440.0; +static const double min_freq = 0.0; +static const double max_freq = 50000000.0; + +static const SpiParamRangeInfo volume_range[] = { + { "min", "Minimum value", sizeof (double), &min_volume }, + { "max", "Maximum value", sizeof (double), &max_volume }, + { NULL, NULL, 0, NULL } +}; + +static const uint32_t wave_val_sine = 0; +static const uint32_t wave_val_square = 1; + +static const SpiParamRangeInfo wave_range[] = { + { "sine", "Sine", sizeof (uint32_t), &wave_val_sine }, + { "square", "Square", sizeof (uint32_t), &wave_val_square }, + { NULL, NULL, 0, NULL } +}; + +static const SpiParamRangeInfo freq_range[] = { + { "min", "Minimum value", sizeof (double), &min_freq }, + { "max", "Maximum value", sizeof (double), &max_freq }, + { NULL, NULL, 0, NULL } +}; + +enum { + PARAM_ID_WAVE, + PARAM_ID_FREQ, + PARAM_ID_VOLUME, + PARAM_ID_LAST, +}; + +static const SpiParamInfo param_info[] = +{ + { PARAM_ID_WAVE, "wave", "Oscillator waveform", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &default_wave, + SPI_PARAM_RANGE_TYPE_ENUM, wave_range, + NULL, + NULL }, + { PARAM_ID_FREQ, "freq", "Frequency of test signal. The sample rate needs to be at least 4 times higher", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_DOUBLE, sizeof (double), + sizeof (double), &default_freq, + SPI_PARAM_RANGE_TYPE_MIN_MAX, freq_range, + NULL, + NULL }, + { PARAM_ID_VOLUME, "volume", "The Volume factor", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_DOUBLE, sizeof (double), + sizeof (double), &default_volume, + SPI_PARAM_RANGE_TYPE_MIN_MAX, volume_range, + NULL, + NULL }, +}; + +#define CHECK_TYPE(type,expected) if (type != expected) return SPI_RESULT_WRONG_PARAM_TYPE; +#define CHECK_SIZE(size,expected) if (size != expected) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_SIZE_RANGE(size,minsize,maxsize) if (size > maxsize || size < minsize) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_SIZE_MAX(size,maxsize) if (size > maxsize) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_UNSET(mask,index) if (mask & (1 << index)) return SPI_RESULT_PARAM_UNSET; + +static SpiResult +enum_param_info (const SpiParams *params, + unsigned int index, + const SpiParamInfo **info) +{ + if (index >= PARAM_ID_LAST) + return SPI_RESULT_ENUM_END; + *info = ¶m_info[index]; + return SPI_RESULT_OK; +} + +static SpiResult +set_param (SpiParams *params, + uint32_t id, + SpiParamType type, + size_t size, + const void *value) +{ + SpiResult res = SPI_RESULT_OK; + SpiAudioTestSrcParams *p = (SpiAudioTestSrcParams *) params; + + if (params == NULL || value == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + switch (id) { + case PARAM_ID_WAVE: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&p->wave, value, size); + break; + case PARAM_ID_FREQ: + CHECK_TYPE (type, SPI_PARAM_TYPE_DOUBLE); + CHECK_SIZE (size, sizeof (double)); + memcpy (&p->freq, value, size); + break; + case PARAM_ID_VOLUME: + CHECK_TYPE (type, SPI_PARAM_TYPE_DOUBLE); + CHECK_SIZE (size, sizeof (double)); + memcpy (&p->volume, value, size); + break; + default: + res = SPI_RESULT_INVALID_PARAM_ID; + break; + } + return res; +} + +static SpiResult +get_param (const SpiParams *params, + uint32_t id, + SpiParamType *type, + size_t *size, + const void **value) +{ + SpiResult res = SPI_RESULT_OK; + SpiAudioTestSrcParams *p = (SpiAudioTestSrcParams *) params; + + if (params == NULL || type == NULL || size == NULL || value == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + switch (id) { + case PARAM_ID_WAVE: + *type = SPI_PARAM_TYPE_UINT32; + *value = &p->wave; + *size = sizeof (uint32_t); + break; + case PARAM_ID_FREQ: + *type = SPI_PARAM_TYPE_DOUBLE; + *value = &p->freq; + *size = sizeof (double); + break; + case PARAM_ID_VOLUME: + *type = SPI_PARAM_TYPE_DOUBLE; + *value = &p->volume; + *size = sizeof (double); + break; + default: + res = SPI_RESULT_INVALID_PARAM_ID; + break; + } + return res; +} + +static void +reset_audiotestsrc_params (SpiAudioTestSrcParams *params) +{ + params->wave = default_wave; + params->freq = default_freq; + params->volume = default_volume; +} + +static SpiResult +spi_audiotestsrc_node_get_params (SpiNode *node, + SpiParams **params) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + + if (node == NULL || params == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + memcpy (&this->tmp_params, &this->params, sizeof (this->tmp_params)); + *params = &this->tmp_params.param; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_set_params (SpiNode *node, + const SpiParams *params) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + SpiAudioTestSrcParams *p = &this->params; + SpiParamType type; + size_t size; + const void *value; + + if (node == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (params == NULL) { + reset_audiotestsrc_params (p); + return SPI_RESULT_OK; + } + + if (params->get_param (params, PARAM_ID_WAVE, &type, &size, &value) == 0) { + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&p->wave, value, size); + } + if (params->get_param (params, PARAM_ID_FREQ, &type, &size, &value) == 0) { + CHECK_TYPE (type, SPI_PARAM_TYPE_DOUBLE); + CHECK_SIZE (size, sizeof (double)); + memcpy (&p->freq, value, size); + } + if (params->get_param (params, PARAM_ID_VOLUME, &type, &size, &value) == 0) { + CHECK_TYPE (type, SPI_PARAM_TYPE_DOUBLE); + CHECK_SIZE (size, sizeof (double)); + memcpy (&p->volume, value, size); + } + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_send_command (SpiNode *node, + SpiCommand *command) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + SpiResult res = SPI_RESULT_NOT_IMPLEMENTED; + + if (node == NULL || command == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + switch (command->type) { + case SPI_COMMAND_INVALID: + res = SPI_RESULT_INVALID_COMMAND; + break; + case SPI_COMMAND_ACTIVATE: + this->last_event.type = SPI_EVENT_TYPE_ACTIVATED; + this->last_event.data = NULL; + this->last_event.size = 0; + this->event = &this->last_event; + res = SPI_RESULT_HAVE_EVENT; + break; + case SPI_COMMAND_DEACTIVATE: + this->last_event.type = SPI_EVENT_TYPE_DEACTIVATED; + this->last_event.data = NULL; + this->last_event.size = 0; + this->event = &this->last_event; + res = SPI_RESULT_HAVE_EVENT; + break; + case SPI_COMMAND_START: + break; + case SPI_COMMAND_STOP: + break; + case SPI_COMMAND_FLUSH: + break; + case SPI_COMMAND_DRAIN: + break; + case SPI_COMMAND_MARKER: + break; + } + return res; +} + +static SpiResult +spi_audiotestsrc_node_get_event (SpiNode *node, + SpiEvent **event) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + + if (node == NULL || event == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (this->event == NULL) + return SPI_RESULT_ERROR; + + *event = this->event; + this->event = NULL; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_set_event_callback (SpiNode *node, + SpiEventCallback event, + void *user_data) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + + if (node == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + this->event_cb = event; + this->user_data = user_data; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_get_n_ports (SpiNode *node, + unsigned int *n_input_ports, + unsigned int *max_input_ports, + unsigned int *n_output_ports, + unsigned int *max_output_ports) +{ + if (node == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (n_input_ports) + *n_input_ports = 0; + if (n_output_ports) + *n_output_ports = 1; + if (max_input_ports) + *max_input_ports = 0; + if (max_output_ports) + *max_output_ports = 1; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_get_port_ids (SpiNode *node, + unsigned int n_input_ports, + uint32_t *input_ids, + unsigned int n_output_ports, + uint32_t *output_ids) +{ + if (node == NULL || input_ids == NULL || output_ids == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (n_output_ports > 0) + output_ids[0] = 0; + + return SPI_RESULT_OK; +} + + +static SpiResult +spi_audiotestsrc_node_add_port (SpiNode *node, + SpiDirection direction, + uint32_t *port_id) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_audiotestsrc_node_remove_port (SpiNode *node, + uint32_t port_id) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static const SpiParamRangeInfo format_format_range[] = { + { "S8", "S8", 2, "S8" }, + { "U8", "U8", 2, "U8" }, + { "S16LE", "S16LE", 5, "S16LE" }, + { "S16BE", "S16BE", 5, "S16BE" }, + { "U16LE", "U16LE", 5, "U16LE" }, + { "U16BE", "U16BE", 5, "U16BE" }, + { "S24_32LE", "S24_32LE", 8, "S24_32LE" }, + { "S24_32BE", "S24_32BE", 8, "S24_32BE" }, + { "U24_32LE", "U24_32LE", 8, "U24_32LE" }, + { "U24_32BE", "U24_32BE", 8, "U24_32BE" }, + { "S32LE", "S32LE", 5, "S32LE" }, + { "S32BE", "S32BE", 5, "S32BE" }, + { "U32LE", "U32LE", 5, "U32LE" }, + { "U32BE", "U32BE", 5, "U32BE" }, + { "S24LE", "S24LE", 5, "S24LE" }, + { "S24BE", "S24BE", 5, "S24BE" }, + { "U24LE", "U24LE", 5, "U24LE" }, + { "U24BE", "U24BE", 5, "U24BE" }, + { "S20LE", "S20LE", 5, "S20LE" }, + { "S20BE", "S20BE", 5, "S20BE" }, + { "U20LE", "U20LE", 5, "U20LE" }, + { "U20BE", "U20BE", 5, "U20BE" }, + { "S18LE", "S18LE", 5, "S18LE" }, + { "S18BE", "S18BE", 5, "S18BE" }, + { "U18LE", "U18LE", 5, "U18LE" }, + { "U18BE", "U18BE", 5, "U18BE" }, + { "F32LE", "F32LE", 5, "F32LE" }, + { "F32BE", "F32BE", 5, "F32BE" }, + { "F64LE", "F64LE", 5, "F64LE" }, + { "F64BE", "F64BE", 5, "F64BE" }, + { NULL, NULL, 0, NULL } +}; + +static const uint32_t min_uint32 = 1; +static const uint32_t max_uint32 = UINT32_MAX; + +static const SpiParamRangeInfo uint32_range[] = { + { "min", "Minimum value", sizeof (uint32_t), &min_uint32 }, + { "max", "Maximum value", sizeof (uint32_t), &max_uint32 }, + { NULL, NULL, 0, NULL } +}; + +enum { + SPI_PARAM_ID_MEDIA_TYPE, + SPI_PARAM_ID_FORMAT, + SPI_PARAM_ID_LAYOUT, + SPI_PARAM_ID_SAMPLERATE, + SPI_PARAM_ID_CHANNELS, + SPI_PARAM_ID_LAST, +}; + +static const int32_t format_default_layout = 1; + +static const SpiParamInfo raw_format_param_info[] = +{ + { SPI_PARAM_ID_MEDIA_TYPE, "media-type", "The media type", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_STRING, 32, + strlen ("audio/x-raw")+1, "audio/x-raw", + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { SPI_PARAM_ID_FORMAT, "format", "The media format", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_STRING, 16, + 0, NULL, + SPI_PARAM_RANGE_TYPE_ENUM, format_format_range, + NULL, + NULL }, + { SPI_PARAM_ID_LAYOUT, "layout", "Sample Layout", + SPI_PARAM_FLAG_READABLE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + sizeof (uint32_t), &format_default_layout, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, + { SPI_PARAM_ID_SAMPLERATE, "rate", "Audio sample rate", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + 0, NULL, + SPI_PARAM_RANGE_TYPE_MIN_MAX, uint32_range, + NULL, + NULL }, + { SPI_PARAM_ID_CHANNELS, "channels", "Audio channels", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_UINT32, sizeof (uint32_t), + 0, NULL, + SPI_PARAM_RANGE_TYPE_MIN_MAX, uint32_range, + NULL, + NULL }, +}; + +static SpiResult +enum_raw_format_param_info (const SpiParams *params, + unsigned int index, + const SpiParamInfo **info) +{ + if (params == NULL || info == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (index >= SPI_PARAM_ID_LAST) + return SPI_RESULT_ENUM_END; + + *info = &raw_format_param_info[index]; + + return SPI_RESULT_OK; +} + +#define CHECK_TYPE(type,expected) if (type != expected) return SPI_RESULT_WRONG_PARAM_TYPE; +#define MARK_SET(mask,index) (mask &= ~(1 << index)) + +static SpiResult +set_format_param (SpiParams *params, + uint32_t id, + SpiParamType type, + size_t size, + const void *value) +{ + SpiAudioTestSrcFormat *f = (SpiAudioTestSrcFormat *) params; + + if (params == NULL || value == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + switch (id) { + case SPI_PARAM_ID_FORMAT: + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 16); + memcpy (f->format, value, size); + MARK_SET (f->unset_mask, 1); + break; + case SPI_PARAM_ID_LAYOUT: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->layout, value, size); + MARK_SET (f->unset_mask, 2); + break; + case SPI_PARAM_ID_SAMPLERATE: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->samplerate, value, size); + MARK_SET (f->unset_mask, 3); + break; + case SPI_PARAM_ID_CHANNELS: + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->channels, value, size); + MARK_SET (f->unset_mask, 4); + break; + default: + return SPI_RESULT_INVALID_PARAM_ID; + } + + return SPI_RESULT_OK; +} + + + +static SpiResult +get_format_param (const SpiParams *params, + uint32_t id, + SpiParamType *type, + size_t *size, + const void **value) +{ + SpiAudioTestSrcFormat *f = (SpiAudioTestSrcFormat *) params; + + if (params == NULL || type == NULL || size == NULL || value == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + switch (id) { + case SPI_PARAM_ID_MEDIA_TYPE: + CHECK_UNSET (f->unset_mask, 0); + *type = SPI_PARAM_TYPE_STRING; + *value = f->media_type; + *size = strlen (f->media_type); + break; + case SPI_PARAM_ID_FORMAT: + CHECK_UNSET (f->unset_mask, 1); + *type = SPI_PARAM_TYPE_STRING; + *value = f->format; + *size = strlen (f->format); + break; + case SPI_PARAM_ID_LAYOUT: + CHECK_UNSET (f->unset_mask, 2); + *type = SPI_PARAM_TYPE_UINT32; + *value = &f->layout; + *size = 4; + break; + case SPI_PARAM_ID_SAMPLERATE: + CHECK_UNSET (f->unset_mask, 3); + *type = SPI_PARAM_TYPE_UINT32; + *value = &f->samplerate; + *size = 4; + break; + case SPI_PARAM_ID_CHANNELS: + CHECK_UNSET (f->unset_mask, 4); + *type = SPI_PARAM_TYPE_UINT32; + *value = &f->channels; + *size = 4; + break; + default: + return SPI_RESULT_INVALID_PARAM_ID; + } + return SPI_RESULT_OK; +} + + +static SpiResult +spi_audiotestsrc_node_enum_port_formats (SpiNode *node, + uint32_t port_id, + unsigned int index, + SpiParams **format) +{ + static SpiAudioTestSrcFormat fmt; + + if (node == NULL || format == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + switch (index) { + case 0: + strcpy (fmt.media_type, "audio/x-raw"); + fmt.unset_mask = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); + fmt.param.enum_param_info = enum_raw_format_param_info; + fmt.param.set_param = set_format_param; + fmt.param.get_param = get_format_param; + break; + default: + return SPI_RESULT_ENUM_END; + } + *format = &fmt.param; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_set_port_format (SpiNode *node, + uint32_t port_id, + int test_only, + const SpiParams *format) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + SpiParamType type; + size_t size; + const void *value; + SpiAudioTestSrcFormat *fmt; + + if (node == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + fmt = &this->current_format; + + if (format == NULL) { + fmt->param.get_param = NULL; + this->have_format = false; + return SPI_RESULT_OK; + } + + if (format->get_param (format, + SPI_PARAM_ID_MEDIA_TYPE, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_MEDIA_TYPE; + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 32); + memcpy (fmt->media_type, value, size); + + if (format->get_param (format, + SPI_PARAM_ID_FORMAT, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_FORMAT_PARAMS; + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 16); + memcpy (fmt->format, value, size); + + if (format->get_param (format, + SPI_PARAM_ID_LAYOUT, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_FORMAT_PARAMS; + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->layout, value, size); + + if (format->get_param (format, + SPI_PARAM_ID_SAMPLERATE, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_FORMAT_PARAMS; + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->samplerate, value, size); + + if (format->get_param (format, + SPI_PARAM_ID_CHANNELS, + &type, &size, &value) < 0) + return SPI_RESULT_INVALID_FORMAT_PARAMS; + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->channels, value, size); + + fmt->param.enum_param_info = enum_raw_format_param_info; + fmt->param.set_param = NULL; + fmt->param.get_param = get_format_param; + this->have_format = true; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_get_port_format (SpiNode *node, + uint32_t port_id, + const SpiParams **format) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + + if (node == NULL || format == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + if (!this->have_format) + return SPI_RESULT_NO_FORMAT; + + *format = &this->current_format.param; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_get_port_info (SpiNode *node, + uint32_t port_id, + SpiPortInfo *info) +{ + if (node == NULL || info == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + info->flags = SPI_PORT_INFO_FLAG_CAN_USE_BUFFER | + SPI_PORT_INFO_FLAG_NO_REF; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_get_port_params (SpiNode *node, + uint32_t port_id, + SpiParams **params) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_audiotestsrc_node_set_port_params (SpiNode *node, + uint32_t port_id, + const SpiParams *params) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_audiotestsrc_node_get_port_status (SpiNode *node, + uint32_t port_id, + SpiPortStatus *status) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + + if (node == NULL || status == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + if (!this->have_format) + return SPI_RESULT_NO_FORMAT; + + status->flags = SPI_PORT_STATUS_FLAG_HAVE_OUTPUT; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_audiotestsrc_node_send_port_data (SpiNode *node, + SpiDataInfo *data) +{ + return SPI_RESULT_INVALID_PORT; +} + +static SpiResult +spi_audiotestsrc_node_receive_port_data (SpiNode *node, + unsigned int n_data, + SpiDataInfo *data) +{ + SpiAudioTestSrc *this = (SpiAudioTestSrc *) node; + size_t i, size; + uint8_t *ptr; + + if (node == NULL || n_data == 0 || data == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (data->port_id != 0) + return SPI_RESULT_INVALID_PORT; + + if (!this->have_format) + return SPI_RESULT_NO_FORMAT; + + if (data->buffer == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + ptr = data->buffer->datas[0].data; + size = data->buffer->datas[0].size; + + for (i = 0; i < size; i++) + ptr[i] = rand(); + + return SPI_RESULT_OK; +} + +SpiNode * +spi_audiotestsrc_new (void) +{ + SpiNode *node; + SpiAudioTestSrc *this; + + node = calloc (1, sizeof (SpiAudioTestSrc)); + + node->get_params = spi_audiotestsrc_node_get_params; + node->set_params = spi_audiotestsrc_node_set_params; + node->send_command = spi_audiotestsrc_node_send_command; + node->get_event = spi_audiotestsrc_node_get_event; + node->set_event_callback = spi_audiotestsrc_node_set_event_callback; + node->get_n_ports = spi_audiotestsrc_node_get_n_ports; + node->get_port_ids = spi_audiotestsrc_node_get_port_ids; + node->add_port = spi_audiotestsrc_node_add_port; + node->remove_port = spi_audiotestsrc_node_remove_port; + node->enum_port_formats = spi_audiotestsrc_node_enum_port_formats; + node->set_port_format = spi_audiotestsrc_node_set_port_format; + node->get_port_format = spi_audiotestsrc_node_get_port_format; + node->get_port_info = spi_audiotestsrc_node_get_port_info; + node->get_port_params = spi_audiotestsrc_node_get_port_params; + node->set_port_params = spi_audiotestsrc_node_set_port_params; + node->get_port_status = spi_audiotestsrc_node_get_port_status; + node->send_port_data = spi_audiotestsrc_node_send_port_data; + node->receive_port_data = spi_audiotestsrc_node_receive_port_data; + + this = (SpiAudioTestSrc *) node; + this->params.param.enum_param_info = enum_param_info; + this->params.param.set_param = set_param; + this->params.param.get_param = get_param; + reset_audiotestsrc_params (&this->params); + + return node; +} diff --git a/pinos/tests/spi-plugins.h b/pinos/tests/spi-plugins.h new file mode 100644 index 000000000..98b14dcd6 --- /dev/null +++ b/pinos/tests/spi-plugins.h @@ -0,0 +1,22 @@ +/* Spi + * Copyright (C) 2016 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. + */ + +SpiNode * spi_volume_new (void); +SpiNode * spi_audiotestsrc_new (void); +SpiNode * spi_alsa_sink_new (void); diff --git a/pinos/tests/spi-volume.c b/pinos/tests/spi-volume.c index d56c204ae..e1475b4f6 100644 --- a/pinos/tests/spi-volume.c +++ b/pinos/tests/spi-volume.c @@ -17,9 +17,12 @@ * Boston, MA 02110-1301, USA. */ -#include +#include + #include +#include "spi-plugins.h" + typedef struct _SpiVolume SpiVolume; typedef struct { @@ -49,7 +52,7 @@ struct _SpiVolume { SpiEvent last_event; SpiEventCallback event_cb; - gpointer user_data; + void *user_data; bool have_format; SpiVolumeFormat current_format; @@ -61,19 +64,20 @@ struct _SpiVolume { }; static const double default_volume = 1.0; -static const uint32_t min_volume = 0.0; -static const uint32_t max_volume = 10.0; +static const double min_volume = 0.0; +static const double max_volume = 10.0; static const bool default_mute = false; static const SpiParamRangeInfo volume_range[] = { - { "min", "Minimum value", 4, &min_volume }, - { "max", "Maximum value", 4, &max_volume }, + { "min", "Minimum value", sizeof (double), &min_volume }, + { "max", "Maximum value", sizeof (double), &max_volume }, { NULL, NULL, 0, NULL } }; enum { PARAM_ID_VOLUME, PARAM_ID_MUTE, + PARAM_ID_LAST, }; static const SpiParamInfo param_info[] = @@ -85,7 +89,7 @@ static const SpiParamInfo param_info[] = SPI_PARAM_RANGE_TYPE_MIN_MAX, volume_range, NULL, NULL }, - { PARAM_ID_MUTE , "mute", "Mute", + { PARAM_ID_MUTE, "mute", "Mute", SPI_PARAM_FLAG_READWRITE, SPI_PARAM_TYPE_BOOL, sizeof (bool), sizeof (bool), &default_mute, @@ -95,22 +99,25 @@ static const SpiParamInfo param_info[] = }; #define CHECK_TYPE(type,expected) if (type != expected) return SPI_RESULT_WRONG_PARAM_TYPE; -#define CHECK_UNSET(mask,idx) if (mask & (1 << idx)) return SPI_RESULT_PARAM_UNSET; +#define CHECK_SIZE(size,expected) if (size != expected) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_SIZE_RANGE(size,minsize,maxsize) if (size > maxsize || size < minsize) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_SIZE_MAX(size,maxsize) if (size > maxsize) return SPI_RESULT_WRONG_PARAM_SIZE; +#define CHECK_UNSET(mask,index) if (mask & (1 << index)) return SPI_RESULT_PARAM_UNSET; static SpiResult -get_param_info (const SpiParams *params, - int idx, - const SpiParamInfo **info) +enum_param_info (const SpiParams *params, + unsigned int index, + const SpiParamInfo **info) { - if (idx < 0 || idx >= 2) - return SPI_RESULT_NO_MORE_PARAM_INFO; - *info = ¶m_info[idx]; + if (index >= PARAM_ID_LAST) + return SPI_RESULT_ENUM_END; + *info = ¶m_info[index]; return SPI_RESULT_OK; } static SpiResult set_param (SpiParams *params, - int id, + uint32_t id, SpiParamType type, size_t size, const void *value) @@ -118,14 +125,19 @@ set_param (SpiParams *params, SpiResult res = SPI_RESULT_OK; SpiVolumeParams *p = (SpiVolumeParams *) params; + if (params == NULL || value == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + switch (id) { - case 0: + case PARAM_ID_VOLUME: CHECK_TYPE (type, SPI_PARAM_TYPE_DOUBLE); - memcpy (&p->volume, value, MIN (sizeof (double), size)); + CHECK_SIZE (size, sizeof (double)); + memcpy (&p->volume, value, size); break; - case 1: + case PARAM_ID_MUTE: CHECK_TYPE (type, SPI_PARAM_TYPE_BOOL); - memcpy (&p->mute, value, MIN (sizeof (bool), size)); + CHECK_SIZE (size, sizeof (bool)); + memcpy (&p->mute, value, size); break; default: res = SPI_RESULT_INVALID_PARAM_ID; @@ -136,7 +148,7 @@ set_param (SpiParams *params, static SpiResult get_param (const SpiParams *params, - int id, + uint32_t id, SpiParamType *type, size_t *size, const void **value) @@ -144,13 +156,16 @@ get_param (const SpiParams *params, SpiResult res = SPI_RESULT_OK; SpiVolumeParams *p = (SpiVolumeParams *) params; + if (params == NULL || type == NULL || size == NULL || value == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + switch (id) { - case 0: + case PARAM_ID_VOLUME: *type = SPI_PARAM_TYPE_DOUBLE; *value = &p->volume; *size = sizeof (double); break; - case 1: + case PARAM_ID_MUTE: *type = SPI_PARAM_TYPE_BOOL; *value = &p->mute; *size = sizeof (bool); @@ -203,14 +218,14 @@ spi_volume_node_set_params (SpiNode *node, } if (params->get_param (params, 0, &type, &size, &value) == 0) { - if (type != SPI_PARAM_TYPE_DOUBLE) - return SPI_RESULT_WRONG_PARAM_TYPE; - memcpy (&p->volume, value, MIN (size, sizeof (double))); + CHECK_TYPE (type, SPI_PARAM_TYPE_DOUBLE); + CHECK_SIZE (size, sizeof (double)); + memcpy (&p->volume, value, size); } if (params->get_param (params, 1, &type, &size, &value) == 0) { - if (type != SPI_PARAM_TYPE_BOOL) - return SPI_RESULT_WRONG_PARAM_TYPE; - memcpy (&p->mute, value, MIN (sizeof (bool), size)); + CHECK_TYPE (type, SPI_PARAM_TYPE_BOOL); + CHECK_SIZE (size, sizeof (bool)); + memcpy (&p->mute, value, size); } return SPI_RESULT_OK; } @@ -432,22 +447,22 @@ static const SpiParamInfo raw_format_param_info[] = }; static SpiResult -get_raw_format_param_info (const SpiParams *params, - int idx, - const SpiParamInfo **info) +enum_raw_format_param_info (const SpiParams *params, + unsigned int index, + const SpiParamInfo **info) { - if (idx < 0 || idx >= 4) - return SPI_RESULT_NO_MORE_PARAM_INFO; - *info = &raw_format_param_info[idx]; + if (index >= 4) + return SPI_RESULT_ENUM_END; + *info = &raw_format_param_info[index]; return SPI_RESULT_OK; } #define CHECK_TYPE(type,expected) if (type != expected) return SPI_RESULT_WRONG_PARAM_TYPE; -#define MARK_SET(mask,idx) (mask &= ~(1 << idx)) +#define MARK_SET(mask,index) (mask &= ~(1 << index)) static SpiResult set_format_param (SpiParams *params, - int id, + uint32_t id, SpiParamType type, size_t size, const void *value) @@ -457,22 +472,26 @@ set_format_param (SpiParams *params, switch (id) { case SPI_PARAM_ID_FORMAT: CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); - memcpy (f->format, value, MIN (16, size)); + CHECK_SIZE_MAX (size, 16); + memcpy (f->format, value, size); MARK_SET (f->unset_mask, 1); break; case SPI_PARAM_ID_LAYOUT: CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); - memcpy (&f->layout, value, MIN (4, size)); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->layout, value, size); MARK_SET (f->unset_mask, 2); break; case SPI_PARAM_ID_SAMPLERATE: CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); - memcpy (&f->samplerate, value, MIN (4, size)); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->samplerate, value, size); MARK_SET (f->unset_mask, 3); break; case SPI_PARAM_ID_CHANNELS: CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); - memcpy (&f->channels, value, MIN (4, size)); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&f->channels, value, size); MARK_SET (f->unset_mask, 4); break; default: @@ -486,7 +505,7 @@ set_format_param (SpiParams *params, static SpiResult get_format_param (const SpiParams *params, - int id, + uint32_t id, SpiParamType *type, size_t *size, const void **value) @@ -532,26 +551,26 @@ get_format_param (const SpiParams *params, static SpiResult -spi_volume_node_get_port_formats (SpiNode *node, - uint32_t port_id, - unsigned int format_idx, - SpiParams **format) +spi_volume_node_enum_port_formats (SpiNode *node, + uint32_t port_id, + unsigned int index, + SpiParams **format) { static SpiVolumeFormat fmt; if (port_id != 0) return SPI_RESULT_INVALID_PORT; - switch (format_idx) { + switch (index) { case 0: strcpy (fmt.media_type, "audio/x-raw"); fmt.unset_mask = (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4); - fmt.param.get_param_info = get_raw_format_param_info; + fmt.param.enum_param_info = enum_raw_format_param_info; fmt.param.set_param = set_format_param; fmt.param.get_param = get_format_param; break; default: - return SPI_RESULT_NO_MORE_FORMATS; + return SPI_RESULT_ENUM_END; } *format = &fmt.param; @@ -583,43 +602,43 @@ spi_volume_node_set_port_format (SpiNode *node, SPI_PARAM_ID_MEDIA_TYPE, &type, &size, &value) < 0) return SPI_RESULT_INVALID_MEDIA_TYPE; - if (type != SPI_PARAM_TYPE_STRING) - return SPI_RESULT_INVALID_MEDIA_TYPE; - memcpy (fmt->media_type, value, MIN (size, 32)); + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 32); + memcpy (fmt->media_type, value, size); if (format->get_param (format, SPI_PARAM_ID_FORMAT, &type, &size, &value) < 0) return SPI_RESULT_INVALID_FORMAT_PARAMS; - if (type != SPI_PARAM_TYPE_STRING) - return SPI_RESULT_INVALID_FORMAT_PARAMS; - memcpy (fmt->format, value, MIN (size, 16)); + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + CHECK_SIZE_MAX (size, 16); + memcpy (fmt->format, value, size); if (format->get_param (format, SPI_PARAM_ID_LAYOUT, &type, &size, &value) < 0) return SPI_RESULT_INVALID_FORMAT_PARAMS; - if (type != SPI_PARAM_TYPE_UINT32) - return SPI_RESULT_INVALID_FORMAT_PARAMS; - memcpy (&fmt->layout, value, MIN (size, 4)); + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->layout, value, size); if (format->get_param (format, SPI_PARAM_ID_SAMPLERATE, &type, &size, &value) < 0) return SPI_RESULT_INVALID_FORMAT_PARAMS; - if (type != SPI_PARAM_TYPE_UINT32) - return SPI_RESULT_INVALID_FORMAT_PARAMS; - memcpy (&fmt->samplerate, value, MIN (size, 4)); + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->samplerate, value, size); if (format->get_param (format, SPI_PARAM_ID_CHANNELS, &type, &size, &value) < 0) return SPI_RESULT_INVALID_FORMAT_PARAMS; - if (type != SPI_PARAM_TYPE_UINT32) - return SPI_RESULT_INVALID_FORMAT_PARAMS; - memcpy (&fmt->channels, value, MIN (size, 4)); + CHECK_TYPE (type, SPI_PARAM_TYPE_UINT32); + CHECK_SIZE (size, sizeof (uint32_t)); + memcpy (&fmt->channels, value, size); - fmt->param.get_param_info = get_raw_format_param_info; + fmt->param.enum_param_info = enum_raw_format_param_info; fmt->param.set_param = NULL; fmt->param.get_param = get_format_param; this->have_format = true; @@ -749,6 +768,8 @@ spi_volume_node_send_port_data (SpiNode *node, return SPI_RESULT_OK; } +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + static SpiResult spi_volume_node_receive_port_data (SpiNode *node, unsigned int n_data, @@ -781,7 +802,7 @@ spi_volume_node_receive_port_data (SpiNode *node, si = di = 0; soff = doff = 0; - while (TRUE) { + while (true) { if (si == sbuf->n_datas || di == dbuf->n_datas) break; @@ -827,7 +848,7 @@ spi_volume_node_receive_port_data (SpiNode *node, return SPI_RESULT_OK; } -static SpiNode * +SpiNode * spi_volume_new (void) { SpiNode *node; @@ -844,7 +865,7 @@ spi_volume_new (void) node->get_port_ids = spi_volume_node_get_port_ids; node->add_port = spi_volume_node_add_port; node->remove_port = spi_volume_node_remove_port; - node->get_port_formats = spi_volume_node_get_port_formats; + node->enum_port_formats = spi_volume_node_enum_port_formats; node->set_port_format = spi_volume_node_set_port_format; node->get_port_format = spi_volume_node_get_port_format; node->get_port_info = spi_volume_node_get_port_info; @@ -855,7 +876,7 @@ spi_volume_new (void) node->receive_port_data = spi_volume_node_receive_port_data; this = (SpiVolume *) node; - this->params.param.get_param_info = get_param_info; + this->params.param.enum_param_info = enum_param_info; this->params.param.set_param = set_param; this->params.param.get_param = get_param; reset_volume_params (&this->params); diff --git a/pinos/tests/test-node.c b/pinos/tests/test-node.c index aa228ad9c..2458ce441 100644 --- a/pinos/tests/test-node.c +++ b/pinos/tests/test-node.c @@ -19,13 +19,16 @@ #include #include -#include +#include +#include -#include -//#include "audio-sink.c" -#include "spi-volume.c" +#include +#include "spi-plugins.h" -static GMainLoop *loop; +typedef struct { + SpiNode *src; + SpiNode *sink; +} AppData; static void print_value (const char *prefix, SpiParamType type, int size, const void *value) @@ -98,8 +101,8 @@ print_params (const SpiParams *params, int print_ranges) const void *value; size_t size; - if ((res = params->get_param_info (params, i, &info)) < 0) { - if (res != SPI_RESULT_NO_MORE_PARAM_INFO) + if ((res = params->enum_param_info (params, i, &info)) < 0) { + if (res != SPI_RESULT_ENUM_END) printf ("got error %d\n", res); break; } @@ -109,7 +112,7 @@ print_params (const SpiParams *params, int print_ranges) printf ("description:\t%s\n", info->description); printf ("flags:\t\t%d\n", info->flags); printf ("type:\t\t%d\n", info->type); - printf ("maxsize:\t%d\n", info->maxsize); + printf ("maxsize:\t%zu\n", info->maxsize); res = params->get_param (params, info->id, &type, &size, &value); if (res == SPI_RESULT_PARAM_UNSET) @@ -148,65 +151,65 @@ inspect_node (SpiNode *node) SpiParams *params; unsigned int n_input, max_input, n_output, max_output, i; SpiParams *format; - const SpiParams *cformat; - uint32_t samplerate; if ((res = node->get_params (node, ¶ms)) < 0) printf ("got error %d\n", res); else print_params (params, 1); - params->set_param (params, 0, SPI_PARAM_TYPE_STRING, 12, "hw:0"); - samplerate = 48000; - params->set_param (params, 3, SPI_PARAM_TYPE_UINT32, 4, &samplerate); - - if ((res = node->set_params (node, params)) < 0) - printf ("got error %d\n", res); - else - print_params (params, 1); - if ((res = node->get_n_ports (node, &n_input, &max_input, &n_output, &max_output)) < 0) printf ("got error %d\n", res); else printf ("supported ports %d %d %d %d\n", n_input, max_input, n_output, max_output); for (i = 0; ; i++) { - uint32_t val; - if ((res = node->get_port_formats (node, 0, i, &format)) < 0) { - if (res != SPI_RESULT_NO_MORE_FORMATS) + if ((res = node->enum_port_formats (node, 0, i, &format)) < 0) { + if (res != SPI_RESULT_ENUM_END) printf ("got error %d\n", res); break; } print_params (format, 1); - - printf ("setting format\n"); - if ((res = format->set_param (format, 2, SPI_PARAM_TYPE_STRING, 5, "S16LE")) < 0) - printf ("got error %d\n", res); - val = 1; - if ((res = format->set_param (format, 3, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0) - printf ("got error %d\n", res); - val = 44100; - if ((res = format->set_param (format, 4, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0) - printf ("got error %d\n", res); - val = 2; - if ((res = format->set_param (format, 5, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0) - printf ("got error %d\n", res); - - if ((res = node->set_port_format (node, 0, 0, format)) < 0) - printf ("set format failed: %d\n", res); } - - if ((res = node->get_port_format (node, 0, &cformat)) < 0) - printf ("got error %d\n", res); - else - print_params (format, 0); - if ((res = node->get_port_params (node, 0, ¶ms)) < 0) - printf ("got error %d\n", res); + printf ("get_port_params error: %d\n", res); else printf ("got params %p\n", params); } +static void +set_params (AppData *data) +{ +} + +static void +set_format (AppData *data) +{ + SpiParams *format; + SpiResult res; + uint32_t val; + + if ((res = data->src->enum_port_formats (data->src, 0, 0, &format)) < 0) + printf ("got error %d\n", res); + + printf ("setting format\n"); + if ((res = format->set_param (format, 1, SPI_PARAM_TYPE_STRING, 5, "S16LE")) < 0) + printf ("got error %d\n", res); + val = 1; + if ((res = format->set_param (format, 2, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0) + printf ("got error %d\n", res); + val = 44100; + if ((res = format->set_param (format, 3, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0) + printf ("got error %d\n", res); + val = 2; + if ((res = format->set_param (format, 4, SPI_PARAM_TYPE_UINT32, 4, &val)) < 0) + printf ("got error %d\n", res); + + if ((res = data->src->set_port_format (data->src, 0, 0, format)) < 0) + printf ("set format failed: %d\n", res); + if ((res = data->sink->set_port_format (data->sink, 0, 0, format)) < 0) + printf ("set format failed: %d\n", res); +} + static void handle_event (SpiNode *node) { @@ -244,6 +247,9 @@ handle_event (SpiNode *node) case SPI_EVENT_TYPE_ERROR: printf ("got error notify\n"); break; + case SPI_EVENT_TYPE_BUFFERING: + printf ("got noffering notify\n"); + break; } } @@ -258,6 +264,7 @@ struct _MyBuffer { uint16_t samples[4096]; }; +#if 0 static MyBuffer my_buffers[4]; static MyBuffer *free_list = NULL; @@ -353,37 +360,16 @@ pull_output (SpiNode *node) return res; } -gint -main (gint argc, gchar *argv[]) +static void +run_volume (SpiNode *node) { - SpiResult res; - SpiCommand cmd; - SpiNode *node; int state; + SpiResult res; - pinos_init (&argc, &argv); - - //node = spi_audio_sink_new (); - node = spi_volume_new (); - - loop = g_main_loop_new (NULL, FALSE); - - inspect_node (node); - - cmd.type = SPI_COMMAND_ACTIVATE; - res = node->send_command (node, &cmd); - switch (res) { - case SPI_RESULT_HAVE_EVENT: - handle_event (node); - break; - default: - break; - } - - setup_buffers (node); + set_params (node); + set_format (node); state = 0; - while (TRUE) { SpiPortStatus status; @@ -416,8 +402,117 @@ main (gint argc, gchar *argv[]) state = 0; } } +} +#endif - g_main_loop_run (loop); +static void +on_event (SpiNode *node, SpiEvent *event, void *user_data) +{ + AppData *data = user_data; + + printf ("got event %d\n", event->type); + + switch (event->type) { + case SPI_EVENT_TYPE_REQUEST_DATA: + { + SpiBuffer *buf; + SpiDataInfo info; + SpiResult res; + + buf = event->data; + + info.port_id = event->port_id; + info.flags = SPI_DATA_FLAG_NONE; + info.buffer = buf; + info.event = NULL; + + if ((res = data->src->receive_port_data (data->src, 1, &info)) < 0) + printf ("got error %d\n", res); + + if ((res = data->sink->send_port_data (data->sink, &info)) < 0) + printf ("got error %d\n", res); + break; + } + default: + break; + } +} + +static void +run_async_sink (AppData *data) +{ + SpiResult res; + SpiCommand cmd; + + set_format (data); + + data->sink->set_event_callback (data->sink, on_event, data); + + cmd.type = SPI_COMMAND_START; + if ((res = data->sink->send_command (data->sink, &cmd)) < 0) + printf ("got error %d\n", res); + + printf ("sleeping for 10 seconds\n"); + sleep (10); + + cmd.type = SPI_COMMAND_STOP; + if ((res = data->sink->send_command (data->sink, &cmd)) < 0) + printf ("got error %d\n", res); +} + +static void +setup_source (AppData *data) +{ + SpiCommand cmd; + SpiResult res; + + data->src = spi_audiotestsrc_new (); + + cmd.type = SPI_COMMAND_ACTIVATE; + if ((res = data->src->send_command (data->src, &cmd)) < 0) + printf ("got error %d\n", res); +} + +static void +setup_sink (AppData *data) +{ + SpiCommand cmd; + SpiResult res; + SpiParams *params; + + data->sink = spi_alsa_sink_new (); + + if ((res = data->sink->get_params (data->sink, ¶ms)) < 0) + printf ("got get_params error %d\n", res); + + params->set_param (params, 0, SPI_PARAM_TYPE_STRING, strlen ("hw:1")+1, "hw:1"); + + if ((res = data->sink->set_params (data->sink, params)) < 0) + printf ("got set_params error %d\n", res); + + cmd.type = SPI_COMMAND_ACTIVATE; + if ((res = data->sink->send_command (data->sink, &cmd)) < 0) + printf ("got error %d\n", res); +} + +int +main (int argc, char *argv[]) +{ + SpiResult res; + SpiCommand cmd; + AppData data; + + setup_source (&data); + setup_sink (&data); + + run_async_sink (&data); + + cmd.type = SPI_COMMAND_DEACTIVATE; + if ((res = data.sink->send_command (data.sink, &cmd)) < 0) + printf ("got error %d\n", res); + cmd.type = SPI_COMMAND_DEACTIVATE; + if ((res = data.src->send_command (data.src, &cmd)) < 0) + printf ("got error %d\n", res); return 0; }