diff --git a/pinos/Makefile.am b/pinos/Makefile.am index ef1f434c6..ea46fc186 100644 --- a/pinos/Makefile.am +++ b/pinos/Makefile.am @@ -131,7 +131,7 @@ noinst_LTLIBRARIES = TESTS_default = -TESTS_norun = test-client +TESTS_norun = test-client test-node # These tests need a running pinos daemon TESTS_daemon = @@ -143,6 +143,11 @@ 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_LDADD = $(AM_LDADD) libpinos-@PINOS_MAJORMINOR@.la +test_node_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS) + ################################### # Tools programs # ################################### diff --git a/pinos/spi/buffer.h b/pinos/spi/buffer.h new file mode 100644 index 000000000..13572f129 --- /dev/null +++ b/pinos/spi/buffer.h @@ -0,0 +1,104 @@ +/* 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_BUFFER_H__ +#define __SPI_BUFFER_H__ + +G_BEGIN_DECLS + +#include + +#include +#include + +typedef struct _SpiBuffer SpiBuffer; + +typedef enum { + SPI_META_TYPE_INVALID = 0, + SPI_META_TYPE_HEADER, +} SpiMetaType; + +typedef struct { + uint32_t flags; + uint32_t seq; + int64_t pts; + int64_t dts_offset; +} SpiMetaHeader; + +typedef struct { + SpiMetaType type; + void *data; + size_t size; +} SpiMeta; + +/** + * SpiDataType: + * @SPI_DATA_TYPE_INVALID: invalid data type, is ignored + * @SPI_DATA_TYPE_MEMPTR: data and size point to memory + * @SPI_DATA_TYPE_FD: data points to SpiDataFd + * @SPI_DATA_TYPE_FD: data points to SpiDataFd + */ +typedef enum { + SPI_DATA_TYPE_INVALID = 0, + SPI_DATA_TYPE_MEMPTR, + SPI_DATA_TYPE_FD, +} SpiDataType; + +typedef struct { + int fd; + int offset; + size_t size; +} SpiDataFD; + +/** + * SpiData: + * @id: user id + * @type: the type of data + * @data: pointer to data + * @size: size of data + */ +typedef struct { + SpiDataType type; + void *data; + size_t size; +} SpiData; + +typedef void (*SpiNotify) (void *data); + +/** + * SpiBuffer: + * @refcount: reference counter + * @notify: called when the refcount reaches 0 + * @size: total size of the buffer + * @n_metas: number of metadata + * @metas: array of @n_metas metadata + * @n_datas: number of data pointers + * @datas: array of @n_datas data pointers + */ +struct _SpiBuffer { + volatile int refcount; + SpiNotify notify; + size_t size; + int n_metas; + SpiMeta *metas; + int n_datas; + SpiData *datas; +}; + +#endif /* __SPI_BUFFER_H__ */ diff --git a/pinos/spi/node.h b/pinos/spi/node.h new file mode 100644 index 000000000..c78c06cc2 --- /dev/null +++ b/pinos/spi/node.h @@ -0,0 +1,234 @@ +/* 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_NODE_H__ +#define __SPI_NODE_H__ + +G_BEGIN_DECLS + +#include + +#include +#include +#include + +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 + */ +typedef enum { + SPI_PORT_INFO_FLAG_NONE = 0, + SPI_PORT_INFO_FLAG_REMOVABLE = 1 << 0, + SPI_PORT_INFO_FLAG_OPTIONAL = 1 << 1, +} 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 + * @features: NULL terminated array of extra port features + * + */ +typedef struct { + SpiPortInfoFlags flags; + int minsize; + int align; + int maxbuffering; + int 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_ACCEPT_INPUT: port accepts input + */ +typedef enum { + SPI_PORT_STATUS_FLAG_NONE = 0, + SPI_PORT_STATUS_FLAG_HAVE_OUTPUT = 1 << 0, + SPI_PORT_STATUS_FLAG_ACCEPT_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_RELEASE_ID, + SPI_EVENT_TYPE_DRAINED, + SPI_EVENT_TYPE_MARKER, + SPI_EVENT_TYPE_ERROR, +} SpiEventType; + +typedef struct { + int n_ids; + void **ids; +} SpiEventReleaseID; + +struct _SpiEvent { + const void *id; + SpiEventType type; + int port_id; + void *data; + size_t size; +}; + +typedef enum { + SPI_DATA_FLAG_NONE = 0, + SPI_DATA_FLAG_FORMAT_CHANGED = (1 << 0), + SPI_DATA_FLAG_PROPERTIES_CHANGED = (1 << 1), + SPI_DATA_FLAG_EOS = (1 << 2), + SPI_DATA_FLAG_NO_BUFFER = (1 << 3), +} SpiDataFlags; + +/** + * SpiDataInfo: + * @port_id: the port id + * @flags: extra flags + * @buffer: a buffer + * @event: an event + */ +typedef struct { + int port_id; + SpiDataFlags flags; + SpiBuffer *buffer; + 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 { + int port_id; + SpiCommandType type; + 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); + +/** + * SpiNodeInterface: + * + * Spi node interface. + */ +struct _SpiNode { + void * user_data; + int size; + + SpiResult (*get_params) (SpiNode *node, + SpiParams **props); + SpiResult (*set_params) (SpiNode *node, + const SpiParams *props); + + SpiResult (*send_command) (SpiNode *node, + SpiCommand *command); + SpiResult (*get_event) (SpiNode *node, + SpiEvent **event); + SpiResult (*set_event_callback) (SpiNode *node, + SpiEventCallback callback, + void *user_data); + + SpiResult (*get_n_ports) (SpiNode *node, + int *n_input_ports, + int *max_input_ports, + int *n_output_ports, + int *max_output_ports); + SpiResult (*get_port_ids) (SpiNode *node, + int n_input_ports, + int *input_ids, + int n_output_ports, + int *output_ids); + + SpiResult (*add_port) (SpiNode *node, + SpiDirection direction, + int *port_id); + SpiResult (*remove_port) (SpiNode *node, + int port_id); + + SpiResult (*get_port_formats) (SpiNode *node, + int port_id, + int format_idx, + SpiParams **format); + SpiResult (*set_port_format) (SpiNode *node, + int port_id, + int test_only, + const SpiParams *format); + SpiResult (*get_port_format) (SpiNode *node, + int port_id, + const SpiParams **format); + + SpiResult (*get_port_info) (SpiNode *node, + int port_id, + SpiPortInfo *info); + + SpiResult (*get_port_params) (SpiNode *node, + int port_id, + SpiParams **params); + SpiResult (*set_port_params) (SpiNode *node, + int port_id, + const SpiParams *params); + + SpiResult (*get_port_status) (SpiNode *node, + int port_id, + SpiPortStatus *status); + + SpiResult (*send_port_data) (SpiNode *node, + SpiDataInfo *data); + SpiResult (*receive_port_data) (SpiNode *node, + int n_data, + SpiDataInfo *data); + +}; + +G_END_DECLS + +#endif /* __SPI_NODE_H__ */ diff --git a/pinos/spi/params.h b/pinos/spi/params.h new file mode 100644 index 000000000..1c6feb0bb --- /dev/null +++ b/pinos/spi/params.h @@ -0,0 +1,194 @@ +/* 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_PARAMS_H__ +#define __SPI_PARAMS_H__ + +G_BEGIN_DECLS + +#include + +typedef struct _SpiParams SpiParams; + +/** + * SpiParamType: + */ +typedef enum { + SPI_PARAM_TYPE_INVALID = 0, + SPI_PARAM_TYPE_BOOL, + SPI_PARAM_TYPE_INT8, + SPI_PARAM_TYPE_UINT8, + SPI_PARAM_TYPE_INT16, + SPI_PARAM_TYPE_UINT16, + SPI_PARAM_TYPE_INT32, + SPI_PARAM_TYPE_UINT32, + SPI_PARAM_TYPE_INT64, + SPI_PARAM_TYPE_UINT64, + SPI_PARAM_TYPE_FLOAT, + SPI_PARAM_TYPE_DOUBLE, + SPI_PARAM_TYPE_STRING, + SPI_PARAM_TYPE_POINTER, + SPI_PARAM_TYPE_FRACTION, + SPI_PARAM_TYPE_BITMASK, + SPI_PARAM_TYPE_BYTES, +} SpiParamType; + +/** + * SpiParamFlags: + * @SPI_PARAM_FLAG_NONE: no flags + * @SPI_PARAM_FLAG_OPTIONAL: the value can be left unset + * @SPI_PARAM_FLAG_READABLE: param is readable + * @SPI_PARAM_FLAG_WRITABLE: param is writable + * @SPI_PARAM_FLAG_READWRITE: param is readable and writable + * @SPI_PARAM_FLAG_DEPRECATED: param is deprecated and should not be used + */ +typedef enum { + SPI_PARAM_FLAG_NONE = 0, + SPI_PARAM_FLAG_OPTIONAL = (1 << 0), + SPI_PARAM_FLAG_READABLE = (1 << 1), + SPI_PARAM_FLAG_WRITABLE = (1 << 2), + SPI_PARAM_FLAG_READWRITE = SPI_PARAM_FLAG_READABLE | SPI_PARAM_FLAG_WRITABLE, + SPI_PARAM_FLAG_DEPRECATED = (1 << 3), +} SpiParamFlags; + +/* SpiParamRangeType: + * @SPI_PARAM_RANGE_TYPE_NONE: no range specified, full range of type applies + * @SPI_PARAM_RANGE_TYPE_MIN_MAX: range contains 2 values, min and max + * @SPI_PARAM_RANGE_TYPE_ENUM: range contains enum of possible values with + * NULL-terminated name + * @SPI_PARAM_RANGE_TYPE_FLAGS: range contains flags of possible values with + * NULL-terminated name + */ +typedef enum { + SPI_PARAM_RANGE_TYPE_NONE = 0, + SPI_PARAM_RANGE_TYPE_MIN_MAX, + SPI_PARAM_RANGE_TYPE_ENUM, + SPI_PARAM_RANGE_TYPE_FLAGS, +} SpiParamRangeType; + +/** + * SpiParamRangeInfo: + * @name: name of this value + * @description: user visible description of this value + * @size: the size of value + * @value: a possible value + */ +typedef struct { + const char *name; + const char *description; + int size; + const void *value; +} SpiParamRangeInfo; + +/** + * SpiParamInfo: + * @id: unique id + * @name: human readable name + * @description: description of the param + * @flags: param flags + * @type: param type + * @max_size: maximum size of param value + * @default_size: size of default value + * @default_value: default value of param + * @range_type: type of the range values + * @range_values: array of possible values + * @tags: extra tags, NULL terminated + * @priv: extra private data + */ +typedef struct { + int id; + const char *name; + const char *description; + SpiParamFlags flags; + SpiParamType type; + int maxsize; + int default_size; + const void *default_value; + SpiParamRangeType range_type; + const SpiParamRangeInfo *range_values; + const char **tags; + const void *priv; +} SpiParamInfo; + +/** + * SpiParams: + * + * Generic parameters. + */ +struct _SpiParams { + /** + * SpiParams::get_param_info: + * @params: a #SpiParams + * @idx: the param index + * @info: pointer to a result #SpiParamInfo + * + * 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 + * at @idx. This can be used to iterate the @params. + */ + SpiResult (*get_param_info) (const SpiParams *params, + int idx, + const SpiParamInfo **infos); + /** + * SpiParams::set_param + * @params: a #SpiParams + * @id: the param id + * @type: the value type to set + * @size: the value size + * @value: the value to set + * + * Sets @value in @param. @type should match the type specified + * in the #SpiParamInfo for @id or else #SPI_RESULT_WRONG_PARAM_TYPE + * is returned. + * + * Returns: #SPI_RESULT_OK on success. + * #SPI_RESULT_INVALID_PARAM_ID when @id is not valid + * #SPI_RESULT_WRONG_PARAM_TYPE when @type is not correct + */ + SpiResult (*set_param) (SpiParams *params, + int id, + SpiParamType type, + size_t size, + const void *value); + /** + * SpiParams::get_param + * @params: a #SpiParams + * @id: the param id + * @type: a location for the value type + * @size: a location for the value size + * @value: a location for the value pointer + * + * Get the type, size and value of the parameter with @id. + * + * Returns: #SPI_RESULT_OK on success. + * #SPI_RESULT_INVALID_PARAM_ID when @id is not valid + * #SPI_RESULT_PARAM_UNSET when no value has been set yet + */ + SpiResult (*get_param) (const SpiParams *params, + int id, + SpiParamType *type, + size_t *size, + const void **value); +}; + +G_END_DECLS + +#endif /* __SPI_PARAMS_H__ */ diff --git a/pinos/spi/result.h b/pinos/spi/result.h new file mode 100644 index 000000000..932516d63 --- /dev/null +++ b/pinos/spi/result.h @@ -0,0 +1,52 @@ +/* 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_RESULT_H__ +#define __SPI_RESULT_H__ + +G_BEGIN_DECLS + +typedef enum { + SPI_RESULT_OK = 0, + SPI_RESULT_ERROR = -1, + SPI_RESULT_INACTIVE = -2, + SPI_RESULT_NO_FORMAT = -3, + SPI_RESULT_INVALID_COMMAND = -4, + SPI_RESULT_INVALID_PORT = -5, + SPI_RESULT_HAVE_ENOUGH_INPUT = -6, + SPI_RESULT_NEED_MORE_INPUT = -7, + SPI_RESULT_HAVE_EVENT = -8, + SPI_RESULT_PORTS_CHANGED = -9, + SPI_RESULT_FORMAT_CHANGED = -10, + SPI_RESULT_PROPERTIES_CHANGED = -11, + SPI_RESULT_NOT_IMPLEMENTED = -12, + SPI_RESULT_INVALID_PARAM_ID = -13, + SPI_RESULT_PARAM_UNSET = -14, + SPI_RESULT_NO_MORE_FORMATS = -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_INVALID_ARGUMENTS = -21, +} SpiResult; + +G_END_DECLS + +#endif /* __SPI_RESULT_H__ */ diff --git a/pinos/tests/spi-volume.c b/pinos/tests/spi-volume.c new file mode 100644 index 000000000..b73052608 --- /dev/null +++ b/pinos/tests/spi-volume.c @@ -0,0 +1,771 @@ +/* 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 + +typedef struct _SpiVolume SpiVolume; + +typedef struct { + SpiParams param; + double volume; + bool mute; +} SpiVolumeParams; + +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]; +} SpiVolumeFormat; + +struct _SpiVolume { + SpiNode node; + + SpiVolumeParams params; + + SpiEvent *event; + SpiEvent last_event; + + SpiEventCallback event_cb; + gpointer user_data; + + bool have_format; + SpiVolumeFormat current_format; + + bool have_input; + SpiBuffer *input_buffer; + + SpiData data; +}; + +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 bool default_mute = false; + +static const SpiParamRangeInfo volume_range[] = { + { "min", "Minimum value", 4, &min_volume }, + { "max", "Maximum value", 4, &max_volume }, + { NULL, NULL, 0, NULL } +}; + +enum { + PARAM_ID_VOLUME, + PARAM_ID_MUTE, +}; + +static const SpiParamInfo param_info[] = +{ + { 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 }, + { PARAM_ID_MUTE , "mute", "Mute", + SPI_PARAM_FLAG_READWRITE, + SPI_PARAM_TYPE_BOOL, sizeof (bool), + sizeof (bool), &default_mute, + SPI_PARAM_RANGE_TYPE_NONE, NULL, + NULL, + NULL }, +}; + +#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; + +static SpiResult +get_param_info (const SpiParams *params, + int idx, + const SpiParamInfo **info) +{ + if (idx < 0 || idx >= 2) + return SPI_RESULT_NO_MORE_PARAM_INFO; + *info = ¶m_info[idx]; + return SPI_RESULT_OK; +} + +static SpiResult +set_param (SpiParams *params, + int id, + SpiParamType type, + size_t size, + const void *value) +{ + SpiResult res = SPI_RESULT_OK; + SpiVolumeParams *p = (SpiVolumeParams *) params; + + switch (id) { + case 0: + CHECK_TYPE (type, SPI_PARAM_TYPE_DOUBLE); + memcpy (&p->volume, value, MIN (sizeof (double), size)); + break; + case 1: + CHECK_TYPE (type, SPI_PARAM_TYPE_BOOL); + memcpy (&p->mute, value, MIN (sizeof (bool), size)); + break; + default: + res = SPI_RESULT_INVALID_PARAM_ID; + break; + } + return res; +} + +static SpiResult +get_param (const SpiParams *params, + int id, + SpiParamType *type, + size_t *size, + const void **value) +{ + SpiResult res = SPI_RESULT_OK; + SpiVolumeParams *p = (SpiVolumeParams *) params; + + switch (id) { + case 0: + *type = SPI_PARAM_TYPE_DOUBLE; + *value = &p->volume; + *size = sizeof (double); + break; + case 1: + *type = SPI_PARAM_TYPE_BOOL; + *value = &p->mute; + *size = sizeof (bool); + break; + default: + res = SPI_RESULT_INVALID_PARAM_ID; + break; + } + return res; +} + +static SpiResult +spi_volume_node_get_params (SpiNode *node, + SpiParams **params) +{ + static SpiVolumeParams p; + SpiVolume *this = (SpiVolume *) node; + + memcpy (&p, &this->params, sizeof (p)); + *params = &p.param; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_volume_node_set_params (SpiNode *node, + const SpiParams *params) +{ + SpiVolume *this = (SpiVolume *) node; + SpiVolumeParams *p = &this->params; + SpiParamType type; + size_t size; + const void *value; + + 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))); + } + 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)); + } + return SPI_RESULT_OK; +} + +static SpiResult +spi_volume_node_send_command (SpiNode *node, + SpiCommand *command) +{ + SpiVolume *this = (SpiVolume *) node; + SpiResult res = SPI_RESULT_NOT_IMPLEMENTED; + + 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_volume_node_get_event (SpiNode *node, + SpiEvent **event) +{ + SpiVolume *this = (SpiVolume *) node; + + if (this->event == NULL) + return SPI_RESULT_ERROR; + + *event = this->event; + this->event = NULL; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_volume_node_set_event_callback (SpiNode *node, + SpiEventCallback event, + void *user_data) +{ + SpiVolume *this = (SpiVolume *) node; + + this->event_cb = event; + this->user_data = user_data; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_volume_node_get_n_ports (SpiNode *node, + int *n_input_ports, + int *max_input_ports, + int *n_output_ports, + int *max_output_ports) +{ + *n_input_ports = 1; + *n_output_ports = 1; + *max_input_ports = 1; + *max_output_ports = 1; + return SPI_RESULT_OK; +} + +static SpiResult +spi_volume_node_get_port_ids (SpiNode *node, + int n_input_ports, + int *input_ids, + int n_output_ports, + int *output_ids) +{ + if (n_input_ports > 0) + input_ids[0] = 0; + if (n_output_ports > 0) + output_ids[0] = 1; + + return SPI_RESULT_OK; +} + + +static SpiResult +spi_volume_node_add_port (SpiNode *node, + SpiDirection direction, + int *port_id) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_volume_node_remove_port (SpiNode *node, + int 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 int32_range[] = { + { "min", "Minimum value", 4, &min_uint32 }, + { "max", "Maximum value", 4, &max_uint32 }, + { NULL, NULL, 0, NULL } +}; + +enum { + SPI_PARAM_ID_INVALID, + SPI_PARAM_ID_MEDIA_TYPE, + SPI_PARAM_ID_FORMAT, + SPI_PARAM_ID_LAYOUT, + SPI_PARAM_ID_SAMPLERATE, + SPI_PARAM_ID_CHANNELS, +}; + +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, + 12, "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 +get_raw_format_param_info (const SpiParams *params, + int idx, + const SpiParamInfo **info) +{ + if (idx < 0 || idx >= 4) + return SPI_RESULT_NO_MORE_PARAM_INFO; + *info = &raw_format_param_info[idx]; + 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)) + +static SpiResult +set_format_param (SpiParams *params, + int id, + SpiParamType type, + size_t size, + const void *value) +{ + SpiVolumeFormat *f = (SpiVolumeFormat *) params; + + switch (id) { + case SPI_PARAM_ID_FORMAT: + CHECK_TYPE (type, SPI_PARAM_TYPE_STRING); + memcpy (f->format, value, MIN (16, 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)); + 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)); + 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)); + 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, + int id, + SpiParamType *type, + size_t *size, + const void **value) +{ + SpiVolumeFormat *f = (SpiVolumeFormat *) 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); + 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_volume_node_get_port_formats (SpiNode *node, + int port_id, + int format_idx, + SpiParams **format) +{ + static SpiVolumeFormat fmt; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + switch (format_idx) { + 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.set_param = set_format_param; + fmt.param.get_param = get_format_param; + break; + default: + return SPI_RESULT_NO_MORE_FORMATS; + } + *format = &fmt.param; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_volume_node_set_port_format (SpiNode *node, + int port_id, + int test_only, + const SpiParams *format) +{ + SpiVolume *this = (SpiVolume *) node; + SpiParamType type; + size_t size; + const void *value; + SpiVolumeFormat *fmt = &this->current_format; + + if (port_id != 0) + return SPI_RESULT_INVALID_PORT; + + 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; + if (type != SPI_PARAM_TYPE_STRING) + return SPI_RESULT_INVALID_MEDIA_TYPE; + memcpy (fmt->media_type, value, MIN (size, 32)); + + 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)); + + 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)); + + 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)); + + 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)); + + fmt->param.get_param_info = get_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_volume_node_get_port_format (SpiNode *node, + int port_id, + const SpiParams **format) +{ + SpiVolume *this = (SpiVolume *) 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_volume_node_get_port_info (SpiNode *node, + int 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_volume_node_get_port_params (SpiNode *node, + int port_id, + SpiParams **params) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_volume_node_set_port_params (SpiNode *node, + int port_id, + const SpiParams *params) +{ + return SPI_RESULT_NOT_IMPLEMENTED; +} + +static SpiResult +spi_volume_node_get_port_status (SpiNode *node, + int port_id, + SpiPortStatus *status) +{ + SpiVolume *this = (SpiVolume *) node; + SpiPortStatusFlags flags = 0; + + if (!this->have_format) + return SPI_RESULT_NO_FORMAT; + + switch (port_id) { + case 0: + if (this->input_buffer == NULL) + flags |= SPI_PORT_STATUS_FLAG_ACCEPT_INPUT; + break; + case 1: + if (this->input_buffer != NULL) + flags |= SPI_PORT_STATUS_FLAG_HAVE_OUTPUT; + break; + default: + return SPI_RESULT_INVALID_PORT; + } + status->flags = flags; + + return SPI_RESULT_OK; +} + +static SpiResult +spi_volume_node_send_port_data (SpiNode *node, + SpiDataInfo *data) +{ + SpiVolume *this = (SpiVolume *) node; + SpiBuffer *buffer = data->buffer; + SpiEvent *event = data->event; + + if (data->port_id != 0) + return SPI_RESULT_INVALID_PORT; + + if (buffer == NULL && event == NULL) + return SPI_RESULT_INVALID_ARGUMENTS; + + if (!this->have_format) + return SPI_RESULT_NO_FORMAT; + + if (buffer) { + if (this->input_buffer != NULL) + return SPI_RESULT_HAVE_ENOUGH_INPUT; + + buffer->refcount++; + this->input_buffer = buffer; + } + if (event) { + switch (event->type) { + default: + break; + } + } + + return SPI_RESULT_OK; +} + +static SpiResult +spi_volume_node_receive_port_data (SpiNode *node, + int n_data, + SpiDataInfo *data) +{ + SpiVolume *this = (SpiVolume *) node; + int i, n_samples; + + if (data->port_id != 1) + return SPI_RESULT_INVALID_PORT; + + if (!this->have_format) + return SPI_RESULT_NO_FORMAT; + + if (this->input_buffer == NULL) + return SPI_RESULT_NEED_MORE_INPUT; + + n_samples = 4096; + + for (i = 0; i < n_samples; i++) { + } + + data->buffer = this->input_buffer; + this->input_buffer = NULL; + + return SPI_RESULT_OK; +} + +static SpiNode * +spi_volume_new (void) +{ + SpiNode *node; + SpiVolume *this; + + node = calloc (1, sizeof (SpiVolume)); + + node->get_params = spi_volume_node_get_params; + node->set_params = spi_volume_node_set_params; + node->send_command = spi_volume_node_send_command; + node->get_event = spi_volume_node_get_event; + node->set_event_callback = spi_volume_node_set_event_callback; + node->get_n_ports = spi_volume_node_get_n_ports; + 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->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; + node->get_port_params = spi_volume_node_get_port_params; + node->set_port_params = spi_volume_node_set_port_params; + node->get_port_status = spi_volume_node_get_port_status; + node->send_port_data = spi_volume_node_send_port_data; + 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.set_param = set_param; + this->params.param.get_param = get_param; + + return node; +} diff --git a/pinos/tests/test-node.c b/pinos/tests/test-node.c new file mode 100644 index 000000000..c07e75759 --- /dev/null +++ b/pinos/tests/test-node.c @@ -0,0 +1,415 @@ +/* Pinos + * 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 +//#include "audio-sink.c" +#include "spi-volume.c" + +static GMainLoop *loop; + +static void +print_value (const char *prefix, SpiParamType type, int size, const void *value) +{ + printf ("%s", prefix); + switch (type) { + case SPI_PARAM_TYPE_INVALID: + printf ("invalid"); + break; + case SPI_PARAM_TYPE_BOOL: + printf ("%s", *(bool *)value ? "true" : "false"); + break; + case SPI_PARAM_TYPE_INT8: + printf ("%" PRIi8, *(int8_t *)value); + break; + case SPI_PARAM_TYPE_UINT8: + printf ("%" PRIu8, *(uint8_t *)value); + break; + case SPI_PARAM_TYPE_INT16: + printf ("%" PRIi16, *(int16_t *)value); + break; + case SPI_PARAM_TYPE_UINT16: + printf ("%" PRIu16, *(uint16_t *)value); + break; + case SPI_PARAM_TYPE_INT32: + printf ("%" PRIi32, *(int32_t *)value); + break; + case SPI_PARAM_TYPE_UINT32: + printf ("%" PRIu32, *(uint32_t *)value); + break; + case SPI_PARAM_TYPE_INT64: + printf ("%" PRIi64 "\n", *(int64_t *)value); + break; + case SPI_PARAM_TYPE_UINT64: + printf ("%" PRIu64 "\n", *(uint64_t *)value); + break; + case SPI_PARAM_TYPE_FLOAT: + printf ("%f", *(float *)value); + break; + case SPI_PARAM_TYPE_DOUBLE: + printf ("%g", *(double *)value); + break; + case SPI_PARAM_TYPE_STRING: + printf ("%s", (char *)value); + break; + case SPI_PARAM_TYPE_POINTER: + printf ("%p", value); + break; + case SPI_PARAM_TYPE_FRACTION: + break; + case SPI_PARAM_TYPE_BITMASK: + break; + case SPI_PARAM_TYPE_BYTES: + break; + default: + break; + } + printf ("\n"); +} + +static void +print_params (const SpiParams *params, int print_ranges) +{ + SpiResult res; + const SpiParamInfo *info; + int i, j; + SpiParamType type; + + for (i = 0; ; i++) { + const void *value; + size_t size; + + if ((res = params->get_param_info (params, i, &info)) < 0) { + if (res != SPI_RESULT_NO_MORE_PARAM_INFO) + printf ("got error %d\n", res); + break; + } + + printf ("id:\t\t%d\n", info->id); + printf ("name:\t\t%s\n", info->name); + 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); + + res = params->get_param (params, info->id, &type, &size, &value); + if (res == SPI_RESULT_PARAM_UNSET) + printf ("value:\t\tunset\n"); + else + print_value ("value:\t\t", type, size, value); + + if (print_ranges) { + if (info->default_value) + print_value ("default:\t", info->type, info->default_size, info->default_value); + else + printf ("default:\tunset\n"); + + printf ("range_type:\t%d\n", info->range_type); + if (info->range_values) { + for (j = 0; info->range_values[j].name; j++) { + const SpiParamRangeInfo *rinfo = &info->range_values[j]; + printf (" name:\t%s\n", rinfo->name); + printf (" description:\t%s\n", rinfo->description); + print_value (" value:\t", info->type, rinfo->size, rinfo->value); + } + } + } + if (info->tags) { + for (j = 0; info->tags[j]; j++) { + printf ("tag:\t%s\n", info->tags[j]); + } + } + } +} + +static void +inspect_node (SpiNode *node) +{ + SpiResult res; + SpiParams *params; + 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) + 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); + else + printf ("got params %p\n", params); +} + +static void +handle_event (SpiNode *node) +{ + SpiEvent *event; + SpiResult res; + + if ((res = node->get_event (node, &event)) < 0) + printf ("got result %d\n", res); + + switch (event->type) { + case SPI_EVENT_TYPE_INVALID: + printf ("got invalid notify\n"); + break; + case SPI_EVENT_TYPE_ACTIVATED: + printf ("got activated notify\n"); + break; + case SPI_EVENT_TYPE_DEACTIVATED: + printf ("got deactivated notify\n"); + break; + case SPI_EVENT_TYPE_HAVE_OUTPUT: + printf ("got have-output notify\n"); + break; + case SPI_EVENT_TYPE_NEED_INPUT: + printf ("got need-input notify\n"); + break; + case SPI_EVENT_TYPE_REQUEST_DATA: + printf ("got request-data notify\n"); + break; + case SPI_EVENT_TYPE_RELEASE_ID: + printf ("got release-id notify\n"); + break; + case SPI_EVENT_TYPE_DRAINED: + printf ("got drained notify\n"); + break; + case SPI_EVENT_TYPE_MARKER: + printf ("got marker notify\n"); + break; + case SPI_EVENT_TYPE_ERROR: + printf ("got error notify\n"); + break; + } +} + +typedef struct _MyBuffer MyBuffer; + +struct _MyBuffer { + SpiBuffer buffer; + SpiMeta meta[1]; + SpiMetaHeader header; + SpiData data[1]; + MyBuffer *next; + uint16_t samples[4096]; +}; + +static MyBuffer my_buffers[4]; +static MyBuffer *free_list = NULL; + +static void +my_buffer_notify (MyBuffer *buffer) +{ + printf ("free buffer %p\n", buffer); + buffer->next = free_list; + free_list = buffer; +} + +static SpiResult +setup_buffers (SpiNode *node) +{ + int i; + + for (i = 0; i < 4; i++) { + my_buffers[i].buffer.refcount = 0; + my_buffers[i].buffer.notify = (SpiNotify) my_buffer_notify; + my_buffers[i].buffer.size = sizeof (MyBuffer); + my_buffers[i].buffer.n_metas = 1; + my_buffers[i].buffer.metas = my_buffers[i].meta; + my_buffers[i].buffer.n_datas = 1; + my_buffers[i].buffer.datas = my_buffers[i].data; + + my_buffers[i].header.flags = 0; + my_buffers[i].header.seq = 0; + my_buffers[i].header.pts = 0; + my_buffers[i].header.dts_offset = 0; + + my_buffers[i].meta[0].type = SPI_META_TYPE_HEADER; + my_buffers[i].meta[0].data = &my_buffers[i].header; + my_buffers[i].meta[0].size = sizeof (my_buffers[i].header); + + my_buffers[i].data[0].type = SPI_DATA_TYPE_MEMPTR; + my_buffers[i].data[0].data = my_buffers[i].samples; + my_buffers[i].data[0].size = sizeof (my_buffers[i].samples); + + my_buffers[i].next = free_list; + free_list = &my_buffers[i]; + } + return SPI_RESULT_OK; +} + +static SpiResult +push_input (SpiNode *node) +{ + SpiResult res; + SpiDataInfo info; + MyBuffer *mybuf; + + mybuf = free_list; + free_list = mybuf->next; + + printf ("alloc buffer %p\n", mybuf); + + mybuf->buffer.refcount = 1; + + info.port_id = 0; + info.flags = SPI_DATA_FLAG_NONE; + info.buffer = &mybuf->buffer; + info.event = NULL; + + if ((res = node->send_port_data (node, &info)) < 0) + printf ("got error %d\n", res); + + if (--mybuf->buffer.refcount == 0) + mybuf->buffer.notify (mybuf); + + return res; +} + +static SpiResult +pull_output (SpiNode *node) +{ + SpiDataInfo info[1] = { { 0, }, }; + SpiResult res; + SpiBuffer *buf; + + info[0].port_id = 1; + info[0].buffer = NULL; + info[0].event = NULL; + + if ((res = node->receive_port_data (node, 1, info)) < 0) + printf ("got error %d\n", res); + + buf = info[0].buffer; + if (buf) { + if (--buf->refcount == 0) + buf->notify (buf); + } + return res; +} + +gint +main (gint argc, gchar *argv[]) +{ + SpiResult res; + SpiCommand cmd; + SpiNode *node; + int state; + + 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); + + state = 0; + + while (TRUE) { + if (state == 0) { + if ((res = push_input (node)) < 0) { + if (res == SPI_RESULT_HAVE_ENOUGH_INPUT) + state = 1; + else { + printf ("got error %d\n", res); + break; + } + } + } + if (state == 1) { + if ((res = pull_output (node)) < 0) { + if (res == SPI_RESULT_NEED_MORE_INPUT) + state = 0; + else { + printf ("got error %d\n", res); + break; + } + } + } + } + + g_main_loop_run (loop); + + return 0; +}