Split out header files

Use separate header files
Add pull-based alsasink
Add audiotestsrc
Implement negotiation and scheduling of audiotestsrc ! alsasink
This commit is contained in:
Wim Taymans 2016-06-03 19:05:19 +02:00
parent 03046301bf
commit 4b2520d173
15 changed files with 2869 additions and 287 deletions

View file

@ -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],

View file

@ -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 #

View file

@ -20,15 +20,14 @@
#ifndef __SPI_BUFFER_H__
#define __SPI_BUFFER_H__
G_BEGIN_DECLS
#include <inttypes.h>
#include <pinos/spi/result.h>
#include <pinos/spi/params.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _SpiBuffer SpiBuffer;
#include <spi/defs.h>
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__ */

55
pinos/spi/command.h Normal file
View file

@ -0,0 +1,55 @@
/* Simple Plugin Interface
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* 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 <spi/defs.h>
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__ */

View file

@ -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 <stdbool.h>
#endif
#include <inttypes.h>
#include <stdlib.h>
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__ */

57
pinos/spi/event.h Normal file
View file

@ -0,0 +1,57 @@
/* Simple Plugin Interface
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* 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 <spi/defs.h>
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__ */

View file

@ -20,94 +20,18 @@
#ifndef __SPI_NODE_H__
#define __SPI_NODE_H__
G_BEGIN_DECLS
#include <inttypes.h>
#include <pinos/spi/result.h>
#include <pinos/spi/params.h>
#include <pinos/spi/buffer.h>
#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 <spi/defs.h>
#include <spi/params.h>
#include <spi/port.h>
#include <spi/event.h>
#include <spi/buffer.h>
#include <spi/command.h>
/**
* 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__ */

View file

@ -20,12 +20,14 @@
#ifndef __SPI_PARAMS_H__
#define __SPI_PARAMS_H__
G_BEGIN_DECLS
#include <pinos/spi/result.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _SpiParams SpiParams;
#include <spi/defs.h>
/**
* 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__ */

91
pinos/spi/port.h Normal file
View file

@ -0,0 +1,91 @@
/* Simple Plugin Interface
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* 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 <spi/defs.h>
/**
* 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__ */

448
pinos/tests/alsa-utils.c Normal file
View file

@ -0,0 +1,448 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include <sys/time.h>
#include <math.h>
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 (&params);
/* 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 (&params);
/* 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;
}

953
pinos/tests/spi-alsa-sink.c Normal file
View file

@ -0,0 +1,953 @@
/* Spi ALSA Sink
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* 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 <spi/node.h>
#include <asoundlib.h>
#include <pthread.h>
#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 = &param_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;
}

View file

@ -0,0 +1,863 @@
/* Spi
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* 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 <string.h>
#include <spi/node.h>
#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 = &param_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;
}

22
pinos/tests/spi-plugins.h Normal file
View file

@ -0,0 +1,22 @@
/* Spi
* Copyright (C) 2016 Wim Taymans <wim.taymans@gmail.com>
*
* 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);

View file

@ -17,9 +17,12 @@
* Boston, MA 02110-1301, USA.
*/
#include <stdbool.h>
#include <string.h>
#include <spi/node.h>
#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 = &param_info[idx];
if (index >= PARAM_ID_LAST)
return SPI_RESULT_ENUM_END;
*info = &param_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);

View file

@ -19,13 +19,16 @@
#include <string.h>
#include <stdio.h>
#include <gio/gio.h>
#include <stdlib.h>
#include <unistd.h>
#include <client/pinos.h>
//#include "audio-sink.c"
#include "spi-volume.c"
#include <spi/node.h>
#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, &params)) < 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, &params)) < 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, &params)) < 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;
}