diff --git a/Makefile.am b/Makefile.am
index 7bcff13..d9e0bcc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,17 +55,20 @@ lib_LTLIBRARIES += src/libpcaudio.la
src_libpcaudio_la_LDFLAGS = -version-info $(LIBPCAUDIO_VERSION) \
${ALSA_LIBS} \
+ ${SNDIO_LIBS} \
${PULSEAUDIO_LIBS} \
${QSA_LIBS} \
${COREAUDIO_LIBS}
src_libpcaudio_la_CFLAGS = ${AM_CFLAGS} \
${ALSA_CFLAGS} \
+ ${SNDIO_CFLAGS} \
${PULSEAUDIO_CFLAGS} \
${COREAUDIO_CFLAGS}
src_libpcaudio_la_SOURCES = \
src/alsa.c \
+ src/sndio.c \
src/qsa.c \
src/oss.c \
src/pulseaudio.c \
diff --git a/README.md b/README.md
index bdfa431..c60de8f 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,7 @@ devices. It supports:
| PulseAudio | Linux |
| QSA | QNX |
| XAudio2 | Windows |
+| sndio | BSD, Linux |
See the [CHANGELOG](CHANGELOG.md) for a description of the changes in the
various releases.
@@ -53,6 +54,7 @@ Optional Libraries:
|----------------|--------------------------------------------|
| alsa | `sudo apt-get install libasound2-dev` |
| pulseaudio | `sudo apt-get install libpulse-dev` |
+| sndio | `sudo apt-get install libsndio-dev` |
### Mac OS
diff --git a/configure.ac b/configure.ac
index 29e62cb..0f065de 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,6 +66,29 @@ AS_IF([test "x$with_alsa" = "xno"], [
AC_SUBST(ALSA_CFLAGS)
AC_SUBST(ALSA_LIBS)
+dnl ================================================================
+dnl sndio checks.
+dnl ================================================================
+
+AC_ARG_WITH([sndio],
+ [AS_HELP_STRING([--with-sndio], [support for sndio audio output @<:@default=yes@:>@])],
+ [])
+
+AS_IF([test "x$with_sndio" = "xno"], [
+ echo "Disabling sndio audio output support";
+ have_sndio=no
+ ], [
+ PKG_CHECK_MODULES(SNDIO, [sndio],
+ [
+ AC_DEFINE(HAVE_SNDIO_H, [], [Do we have sndio])
+ have_sndio=yes
+ ],[
+ have_sndio=no
+ ])])
+
+AC_SUBST(SNDIO_CFLAGS)
+AC_SUBST(SNDIO_LIBS)
+
dnl ================================================================
dnl QSA checks.
dnl ================================================================
@@ -172,4 +195,5 @@ AC_MSG_NOTICE([
QSA support: ${have_qsa}
Coreaudio support: ${have_coreaudio}
OSS support: ${have_oss}
+ sndio support: ${have_sndio}
])
diff --git a/src/audio.c b/src/audio.c
index 745d7b6..d5952fc 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -98,6 +98,8 @@ create_audio_device_object(const char *device,
#else
if ((object = create_pulseaudio_object(device, application_name, description)) != NULL)
return object;
+ if ((object = create_sndio_object(device, application_name, description)) != NULL)
+ return object;
if ((object = create_alsa_object(device, application_name, description)) != NULL)
return object;
if ((object = create_qsa_object(device, application_name, description)) != NULL)
diff --git a/src/audio_priv.h b/src/audio_priv.h
index 669a037..633da4a 100644
--- a/src/audio_priv.h
+++ b/src/audio_priv.h
@@ -100,6 +100,11 @@ create_pulseaudio_object(const char *device,
const char *application_name,
const char *description);
+struct audio_object *
+create_sndio_object(const char *device,
+ const char *application_name,
+ const char *description);
+
struct audio_object *
create_alsa_object(const char *device,
const char *application_name,
diff --git a/src/sndio.c b/src/sndio.c
new file mode 100644
index 0000000..69f2bff
--- /dev/null
+++ b/src/sndio.c
@@ -0,0 +1,284 @@
+/* sndio Output.
+ *
+ * Copyright (C) 2021 Haelwenn (lanodan) Monnier
+ *
+ * This file is part of pcaudiolib.
+ *
+ * pcaudiolib is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * pcaudiolib 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with pcaudiolib. If not, see .
+ */
+
+#include "config.h"
+#include "audio_priv.h"
+
+#ifdef HAVE_SNDIO_H
+
+#include
+#include /* strdup */
+
+struct sndio_object
+{
+ struct audio_object vtable;
+ struct sio_hdl *hdl;
+ char *application_name;
+ char *description;
+};
+
+#define to_sndio_object(object) container_of(object, struct sndio_object, vtable)
+
+int
+audio_format_get_bits(enum audio_object_format format) {
+ switch(format)
+ {
+ case AUDIO_OBJECT_FORMAT_S8:
+ case AUDIO_OBJECT_FORMAT_U8:
+ return 8;
+ break;
+
+ case AUDIO_OBJECT_FORMAT_S16LE:
+ case AUDIO_OBJECT_FORMAT_S16BE:
+ case AUDIO_OBJECT_FORMAT_U16LE:
+ case AUDIO_OBJECT_FORMAT_U16BE:
+ return 16;
+ break;
+
+ case AUDIO_OBJECT_FORMAT_S18LE:
+ case AUDIO_OBJECT_FORMAT_S18BE:
+ case AUDIO_OBJECT_FORMAT_U18LE:
+ case AUDIO_OBJECT_FORMAT_U18BE:
+ return 18;
+ break;
+
+ case AUDIO_OBJECT_FORMAT_S20LE:
+ case AUDIO_OBJECT_FORMAT_S20BE:
+ case AUDIO_OBJECT_FORMAT_U20LE:
+ case AUDIO_OBJECT_FORMAT_U20BE:
+ return 20;
+ break;
+
+ case AUDIO_OBJECT_FORMAT_S24LE:
+ case AUDIO_OBJECT_FORMAT_S24BE:
+ case AUDIO_OBJECT_FORMAT_U24LE:
+ case AUDIO_OBJECT_FORMAT_U24BE:
+ return 24;
+ break;
+
+ case AUDIO_OBJECT_FORMAT_S24_32LE:
+ case AUDIO_OBJECT_FORMAT_S24_32BE:
+ case AUDIO_OBJECT_FORMAT_U24_32LE:
+ case AUDIO_OBJECT_FORMAT_U24_32BE:
+ return -1; // ???
+ break;
+
+ case AUDIO_OBJECT_FORMAT_S32LE:
+ case AUDIO_OBJECT_FORMAT_S32BE:
+ case AUDIO_OBJECT_FORMAT_U32LE:
+ case AUDIO_OBJECT_FORMAT_U32BE:
+ return 32;
+ break;
+ default:
+ return -1;
+ }
+}
+
+int
+audio_format_is_sig(enum audio_object_format format) {
+ switch(format)
+ {
+ case AUDIO_OBJECT_FORMAT_S8:
+ case AUDIO_OBJECT_FORMAT_S16LE:
+ case AUDIO_OBJECT_FORMAT_S16BE:
+ case AUDIO_OBJECT_FORMAT_S18LE:
+ case AUDIO_OBJECT_FORMAT_S18BE:
+ case AUDIO_OBJECT_FORMAT_S20LE:
+ case AUDIO_OBJECT_FORMAT_S20BE:
+ case AUDIO_OBJECT_FORMAT_S24LE:
+ case AUDIO_OBJECT_FORMAT_S24BE:
+ case AUDIO_OBJECT_FORMAT_S24_32LE:
+ case AUDIO_OBJECT_FORMAT_S24_32BE:
+ case AUDIO_OBJECT_FORMAT_S32LE:
+ case AUDIO_OBJECT_FORMAT_S32BE:
+ return 1;
+ case AUDIO_OBJECT_FORMAT_U8:
+ case AUDIO_OBJECT_FORMAT_U16LE:
+ case AUDIO_OBJECT_FORMAT_U16BE:
+ case AUDIO_OBJECT_FORMAT_U18LE:
+ case AUDIO_OBJECT_FORMAT_U18BE:
+ case AUDIO_OBJECT_FORMAT_U20LE:
+ case AUDIO_OBJECT_FORMAT_U20BE:
+ case AUDIO_OBJECT_FORMAT_U24LE:
+ case AUDIO_OBJECT_FORMAT_U24BE:
+ case AUDIO_OBJECT_FORMAT_U24_32LE:
+ case AUDIO_OBJECT_FORMAT_U24_32BE:
+ case AUDIO_OBJECT_FORMAT_U32LE:
+ case AUDIO_OBJECT_FORMAT_U32BE:
+ return 0;
+ break;
+ default:
+ return -1;
+ }
+}
+
+int
+audio_format_is_le(enum audio_object_format format) {
+ switch(format)
+ {
+ case AUDIO_OBJECT_FORMAT_S8:
+ case AUDIO_OBJECT_FORMAT_U8:
+ case AUDIO_OBJECT_FORMAT_S16BE:
+ case AUDIO_OBJECT_FORMAT_U16BE:
+ case AUDIO_OBJECT_FORMAT_S18BE:
+ case AUDIO_OBJECT_FORMAT_U18BE:
+ case AUDIO_OBJECT_FORMAT_S20BE:
+ case AUDIO_OBJECT_FORMAT_U20BE:
+ case AUDIO_OBJECT_FORMAT_S24BE:
+ case AUDIO_OBJECT_FORMAT_U24BE:
+ case AUDIO_OBJECT_FORMAT_S24_32BE:
+ case AUDIO_OBJECT_FORMAT_U24_32BE:
+ return 0;
+
+ case AUDIO_OBJECT_FORMAT_S16LE:
+ case AUDIO_OBJECT_FORMAT_U16LE:
+ case AUDIO_OBJECT_FORMAT_S18LE:
+ case AUDIO_OBJECT_FORMAT_U18LE:
+ case AUDIO_OBJECT_FORMAT_S20LE:
+ case AUDIO_OBJECT_FORMAT_U20LE:
+ case AUDIO_OBJECT_FORMAT_S24LE:
+ case AUDIO_OBJECT_FORMAT_U24LE:
+ case AUDIO_OBJECT_FORMAT_S24_32LE:
+ case AUDIO_OBJECT_FORMAT_U24_32LE:
+ return 1;
+
+ default:
+ return -1;
+ }
+}
+
+int
+sndio_object_open(struct audio_object *object,
+ enum audio_object_format format,
+ uint32_t rate,
+ uint8_t channels)
+{
+ struct sndio_object *self = to_sndio_object(object);
+ struct sio_par par;
+ if (self->hdl)
+ return -1;
+
+ self->hdl = sio_open(self->application_name, SIO_PLAY, 0);
+ if(!self->hdl) {
+ return -1;
+ }
+
+ sio_initpar(&par);
+
+ int bits = audio_format_get_bits(format);
+ if(bits != -1) {
+ par.bits = bits;
+ }
+
+ int sig = audio_format_is_sig(format);
+ if(sig != -1) {
+ par.sig = sig;
+ }
+
+ int le = audio_format_is_le(format);
+ if(le != -1) {
+ par.le = le;
+ }
+
+ par.rate = rate;
+ par.rchan = 0;
+ par.pchan = channels;
+
+ if(!sio_setpar(self->hdl, &par))
+ return -1;
+
+ if(!sio_getpar(self->hdl, &par))
+ return -1;
+
+ if(par.bits != bits)
+ return -1;
+ if(par.sig != sig)
+ return -1;
+ if(par.le != le)
+ return -1;
+
+ return 0;
+}
+
+void
+sndio_object_close(struct audio_object *object)
+{
+ struct sndio_object *self = to_sndio_object(object);
+
+ if (self->hdl) {
+ sio_close(self->hdl);
+ self->hdl = NULL;
+ }
+}
+
+void
+sndio_object_destroy(struct audio_object *object)
+{
+ struct sndio_object *self = to_sndio_object(object);
+
+ free(self->application_name);
+ free(self->description);
+ free(self);
+}
+
+int
+sndio_object_write(struct audio_object *object,
+ const void *data,
+ size_t bytes)
+{
+ struct sndio_object *self = to_sndio_object(object);
+ if (!self->hdl)
+ return 0;
+
+ return sio_write(self->hdl, data, bytes) == bytes;
+}
+
+struct audio_object *
+create_sndio_object(const char *device,
+ const char *application_name,
+ const char *description)
+{
+ struct sndio_object *self = malloc(sizeof(struct sndio_object));
+ if (!self)
+ return NULL;
+
+ self->hdl = NULL;
+ self->application_name = application_name ? strdup(application_name) : NULL;
+ self->description = description ? strdup(description) : NULL;
+
+ self->vtable.open = sndio_object_open;
+ self->vtable.close = sndio_object_close;
+ self->vtable.destroy = sndio_object_destroy;
+ self->vtable.write = sndio_object_write;
+
+ return &self->vtable;
+}
+
+#else
+
+struct audio_object *
+create_sndio_object(const char *device,
+ const char *application_name,
+ const char *description)
+{
+ return NULL;
+}
+
+#endif