New port: audio/sndio

Sndio is a small audio and MIDI framework part of the OpenBSD project.

It provides an lightweight audio & MIDI server and a fully documented
user-space API to access either the server or directly the hardware in
a uniform way.  Sndio is designed to work for desktop applications,
but pays special attention to synchronization mechanisms and
reliability required by music applications.  Reliability through
simplicity are part of the project goals.

WWW: http://www.sndio.org/

PR:		210124
Submitted by:	Tobias Kortkamp <t@tobik.me>
This commit is contained in:
Kurt Jaeger 2016-08-03 06:15:41 +00:00
parent 7f5840ad26
commit 026baf8a41
Notes: svn2git 2021-03-31 03:12:20 +00:00
svn path=/head/; revision=419497
14 changed files with 1155 additions and 0 deletions

1
GIDs
View File

@ -234,6 +234,7 @@ aox:*:666:
riak:*:667:
bnetd:*:700:
fastnetmon:*:701:
_sndio:*:702:
bopm:*:717:
openxpki:*:777:
zetacoin:*:780:

1
UIDs
View File

@ -242,6 +242,7 @@ riakcs:*:668:667::0:0:Riak CS user:/usr/local/lib/riak-cs:/bin/sh
stanchion:*:669:667::0:0:Stanchion user:/usr/local/lib/stanchion:/bin/sh
bnetd:*:700:700::0:0:Bnetd user:/nonexistent:/usr/sbin/nologin
fastnetmon:*:701:701::0:0:FastNetMon user:/nonexistent:/usr/sbin/nologin
_sndio:*:702:702::0:0:sndio privsep:/var/empty:/usr/sbin/nologin
bopm:*:717:717::0:0:Blitzed Open Proxy Monitor:/nonexistent:/bin/sh
_dnscrypt-wrapper:*:718:65534::0:0:dnscrypt-wrapper user:/var/empty:/usr/sbin/nologin
openxpki:*:777:777::0:0:OpenXPKI Owner:/nonexistent:/usr/sbin/nologin

View File

@ -697,6 +697,7 @@
SUBDIR += smasher
SUBDIR += snack
SUBDIR += snd
SUBDIR += sndio
SUBDIR += solfege
SUBDIR += sonata
SUBDIR += sooperlooper

41
audio/sndio/Makefile Normal file
View File

@ -0,0 +1,41 @@
# Created by: Tobias Kortkamp <t@tobik.me>
# $FreeBSD$
PORTNAME= sndio
PORTVERSION= 1.1.0
CATEGORIES= audio
MASTER_SITES= http://www.sndio.org/
MAINTAINER= t@tobik.me
COMMENT= Small audio and MIDI framework from the OpenBSD project
LICENSE= ISCL
HAS_CONFIGURE= yes
CONFIGURE_ARGS= --prefix=${PREFIX} --mandir=${PREFIX}/man
USE_LDCONFIG= yes
USE_RC_SUBR= sndiod
.include <bsd.port.pre.mk>
# FreeBSD 9.x does not have SOCK_CLOEXEC
.if ${OSVERSION} < 1000000
CFLAGS+= -DSOCK_CLOEXEC=0
.endif
USERS= _sndio
GROUPS= _sndio
# Parallel build leads to problems, but sndio is very quick to compile
# as is so not worth fixing
MAKE_JOBS_UNSAFE= yes
post-install:
@${STRIP_CMD} \
${STAGEDIR}${PREFIX}/lib/libsndio.so.6.1 \
${STAGEDIR}${PREFIX}/bin/sndiod \
${STAGEDIR}${PREFIX}/bin/aucat \
${STAGEDIR}${PREFIX}/bin/midicat
.include <bsd.port.post.mk>

3
audio/sndio/distinfo Normal file
View File

@ -0,0 +1,3 @@
TIMESTAMP = 1465315037
SHA256 (sndio-1.1.0.tar.gz) = fcd7f845ff70f38c2898d737450b8aa3e1bb0afb9d147e8429ef22c0b2c2db57
SIZE (sndio-1.1.0.tar.gz) = 121018

View File

@ -0,0 +1,60 @@
--- configure.orig 2015-12-15 05:28:04 UTC
+++ configure
@@ -32,6 +32,7 @@ prefix=/usr/local # where to install s
so="libsndio.so.\${MAJ}.\${MIN}" # shared libs to build
alsa=no # do we want alsa support ?
sun=no # do we want sun support ?
+oss=no # do we want oss support ?
rmidi=no # do we want support for raw char dev ?
precision=16 # aucat/sndiod arithmetic precision
user=_sndio # non-privileged user for sndio daemon
@@ -71,6 +72,14 @@ case `uname` in
defs='-DHAVE_ARC4RANDOM -DHAVE_ISSETUGID \\\
-DHAVE_STRLCAT -DHAVE_STRLCPY -DHAVE_STRTONUM'
;;
+ FreeBSD)
+ user=_sndio
+ so="$so libsndio.so"
+ defs='-DHAVE_ARC4RANDOM -DHAVE_ISSETUGID \\\
+ -DHAVE_STRLCAT -DHAVE_STRLCPY -DHAVE_STRTONUM'
+ oss=yes
+ mandir=${prefix}/man
+ ;;
esac
# shell word separator (none)
@@ -106,6 +115,12 @@ for i; do
--disable-alsa)
alsa=no
shift;;
+ --enable-oss)
+ oss=yes
+ shift;;
+ --disable-oss)
+ oss=no
+ shift;;
--enable-sun)
sun=yes
shift;;
@@ -162,6 +177,13 @@ if [ $alsa = yes ]; then
fi
#
+# if using OSS, add corresponding parameters
+#
+if [ $oss = yes ]; then
+ defs="$defs -DUSE_OSS"
+fi
+
+#
# if using Sun API, add corresponding parameters
#
if [ $sun = yes ]; then
@@ -215,6 +237,7 @@ user..................... $user
libbsd................... $libbsd
precision................ $precision
alsa..................... $alsa
+oss...................... $oss
sun...................... $sun
rmidi.................... $rmidi

View File

@ -0,0 +1,17 @@
--- libsndio/Makefile.in.orig 2015-12-30 11:54:40 UTC
+++ libsndio/Makefile.in
@@ -99,7 +99,7 @@ clean:
#
OBJS = debug.o aucat.o \
mio.o mio_rmidi.o mio_alsa.o mio_aucat.o \
-sio.o sio_alsa.o sio_aucat.o sio_sun.o \
+sio.o sio_alsa.o sio_aucat.o sio_oss.o sio_sun.o \
issetugid.o strlcat.o strlcpy.o strtonum.o
.c.o:
@@ -140,3 +140,5 @@ sio_aucat.o: sio_aucat.c aucat.h amsg.h
../bsd-compat/bsd-compat.h
sio_sun.o: sio_sun.c debug.h sio_priv.h sndio.h \
../bsd-compat/bsd-compat.h
+sio_oss.o: sio_oss.c debug.h sio_priv.h sndio.h \
+ ../bsd-compat/bsd-compat.h

View File

@ -0,0 +1,20 @@
--- libsndio/sio.c.orig 2016-01-08 20:51:12 UTC
+++ libsndio/sio.c
@@ -64,6 +64,8 @@ sio_open(const char *str, unsigned int m
return hdl;
#if defined(USE_SUN)
return _sio_sun_open("rsnd/0", mode, nbio);
+#elif defined(USE_OSS)
+ return _sio_oss_open("rsnd/0", mode, nbio);
#elif defined(USE_ALSA)
return _sio_alsa_open("rsnd/0", mode, nbio);
#else
@@ -75,6 +77,8 @@ sio_open(const char *str, unsigned int m
if (_sndio_parsetype(str, "rsnd"))
#if defined(USE_SUN)
return _sio_sun_open(str, mode, nbio);
+#elif defined(USE_OSS)
+ return _sio_oss_open(str, mode, nbio);
#elif defined(USE_ALSA)
return _sio_alsa_open(str, mode, nbio);
#else

View File

@ -0,0 +1,893 @@
--- libsndio/sio_oss.c.orig 2016-07-29 14:09:21 UTC
+++ libsndio/sio_oss.c
@@ -0,0 +1,890 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef USE_OSS
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "sio_priv.h"
+#include "bsd-compat.h"
+
+#define DEVPATH_PREFIX "/dev/dsp"
+#define DEVPATH_MAX (1 + \
+ sizeof(DEVPATH_PREFIX) - 1 + \
+ sizeof(int) * 3)
+
+struct audio_pos {
+ unsigned int play_pos; /* total bytes played */
+ unsigned int play_xrun; /* bytes of silence inserted */
+ unsigned int rec_pos; /* total bytes recorded */
+ unsigned int rec_xrun; /* bytes dropped */
+};
+
+#define AUDIO_INITPAR(p) \
+ (void)memset((void *)(p), 0xff, sizeof(struct audio_swpar))
+
+/*
+ * argument to AUDIO_SETPAR and AUDIO_GETPAR ioctls
+ */
+struct audio_swpar {
+ unsigned int sig; /* if 1, encoding is signed */
+ unsigned int le; /* if 1, encoding is little-endian */
+ unsigned int bits; /* bits per sample */
+ unsigned int bps; /* bytes per sample */
+ unsigned int msb; /* if 1, bits are msb-aligned */
+ unsigned int rate; /* common play & rec sample rate */
+ unsigned int pchan; /* play channels */
+ unsigned int rchan; /* rec channels */
+ unsigned int nblks; /* number of blocks in play buffer */
+ unsigned int round; /* common frames per block */
+ unsigned int _spare[6];
+};
+
+struct sio_oss_hdl {
+ struct sio_hdl sio;
+ int fd;
+ int filling;
+ unsigned int ibpf, obpf; /* bytes per frame */
+ unsigned int ibytes, obytes; /* bytes the hw transferred */
+ unsigned int ierr, oerr; /* frames the hw dropped */
+ int idelta, odelta; /* position reported to client */
+
+ unsigned int play_pos;
+ struct audio_swpar swpar;
+};
+
+static void sio_oss_close(struct sio_hdl *);
+static int sio_oss_start(struct sio_hdl *);
+static int sio_oss_stop(struct sio_hdl *);
+static int sio_oss_setpar(struct sio_hdl *, struct sio_par *);
+static int sio_oss_getpar(struct sio_hdl *, struct sio_par *);
+static int sio_oss_getcap(struct sio_hdl *, struct sio_cap *);
+static size_t sio_oss_read(struct sio_hdl *, void *, size_t);
+static size_t sio_oss_write(struct sio_hdl *, const void *, size_t);
+static int sio_oss_nfds(struct sio_hdl *);
+static int sio_oss_pollfd(struct sio_hdl *, struct pollfd *, int);
+static int sio_oss_revents(struct sio_hdl *, struct pollfd *);
+
+static void sio_oss_fmt_to_swpar(int, struct audio_swpar *);
+static int sio_oss_audio_getpos(struct sio_oss_hdl *, struct audio_pos *);
+static int sio_oss_audio_getpar(struct sio_oss_hdl *, struct audio_swpar *);
+static int sio_oss_audio_setpar(struct sio_oss_hdl *, struct audio_swpar *);
+static int sio_oss_audio_start(struct sio_oss_hdl *);
+static int sio_oss_audio_stop(struct sio_oss_hdl *, int);
+
+static struct sio_ops sio_oss_ops = {
+ sio_oss_close,
+ sio_oss_setpar,
+ sio_oss_getpar,
+ sio_oss_getcap,
+ sio_oss_write,
+ sio_oss_read,
+ sio_oss_start,
+ sio_oss_stop,
+ sio_oss_nfds,
+ sio_oss_pollfd,
+ sio_oss_revents,
+ NULL, /* setvol */
+ NULL, /* getvol */
+};
+
+static int
+sio_oss_adjpar(struct sio_oss_hdl *hdl, struct audio_swpar *ap)
+{
+ if (hdl->sio.eof)
+ return 0;
+ if (sio_oss_audio_setpar(hdl, ap)) {
+ DPERROR("AUDIO_SETPAR");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ if (sio_oss_audio_getpar(hdl, ap)) {
+ DPERROR("AUDIO_GETPAR");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * try to set the device to the given parameters and check that the
+ * device can use them; return 1 on success, 0 on failure or error
+ */
+static int
+sio_oss_testpar(struct sio_oss_hdl *hdl, struct sio_enc *enc,
+ unsigned int pchan, unsigned int rchan, unsigned int rate)
+{
+ struct audio_swpar ap;
+
+ AUDIO_INITPAR(&ap);
+ if (enc != NULL) {
+ ap.sig = enc->sig;
+ ap.bits = enc->bits;
+ ap.bps = enc->bps;
+ if (ap.bps > 1)
+ ap.le = enc->le;
+ if (ap.bps * 8 > ap.bits)
+ ap.msb = enc->msb;
+ }
+ if (rate)
+ ap.rate = rate;
+ if (pchan && (hdl->sio.mode & SIO_PLAY))
+ ap.pchan = pchan;
+ if (rchan && (hdl->sio.mode & SIO_REC))
+ ap.rchan = rchan;
+ if (!sio_oss_adjpar(hdl, &ap))
+ return 0;
+ if (pchan && ap.pchan != pchan)
+ return 0;
+ if (rchan && ap.rchan != rchan)
+ return 0;
+ if (rate && ap.rate != rate)
+ return 0;
+ if (enc) {
+ if (ap.sig != enc->sig)
+ return 0;
+ if (ap.bits != enc->bits)
+ return 0;
+ if (ap.bps != enc->bps)
+ return 0;
+ if (ap.bps > 1 && ap.le != enc->le)
+ return 0;
+ if (ap.bits < ap.bps * 8 && ap.msb != enc->msb)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * guess device capabilities
+ */
+static int
+sio_oss_getcap(struct sio_hdl *sh, struct sio_cap *cap)
+{
+ static unsigned int chans[] = {
+ 1, 2, 4, 6, 8, 10, 12
+ };
+ static unsigned int rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000,
+ 32000, 44100, 48000, 64000, 88200, 96000
+ };
+ static unsigned int encs[] = {
+ 8, 16, 24, 32
+ };
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+ struct audio_swpar savepar, ap;
+ unsigned int nconf = 0;
+ unsigned int enc_map = 0, rchan_map = 0, pchan_map = 0, rate_map;
+ unsigned int i, j, conf;
+
+ if (sio_oss_audio_getpar(hdl, &savepar)) {
+ DPERROR("AUDIO_GETPAR");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+
+ /*
+ * get a subset of supported encodings
+ */
+ for (i = 0; i < sizeof(encs) / sizeof(encs[0]); i++) {
+ AUDIO_INITPAR(&ap);
+ ap.bits = encs[i];
+ ap.sig = (ap.bits > 8) ? 1 : 0;
+ if (!sio_oss_adjpar(hdl, &ap))
+ return 0;
+ if (ap.bits == encs[i]) {
+ cap->enc[i].sig = ap.sig;
+ cap->enc[i].bits = ap.bits;
+ cap->enc[i].le = ap.le;
+ cap->enc[i].bps = ap.bps;
+ cap->enc[i].msb = ap.msb;
+ enc_map |= 1 << i;
+ }
+ }
+
+ /*
+ * fill channels
+ *
+ * for now we're lucky: all kernel devices assume that the
+ * number of channels and the encoding are independent so we can
+ * use the current encoding and try various channels.
+ */
+ if (hdl->sio.mode & SIO_PLAY) {
+ for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
+ AUDIO_INITPAR(&ap);
+ ap.pchan = chans[i];
+ if (!sio_oss_adjpar(hdl, &ap))
+ return 0;
+ if (ap.pchan == chans[i]) {
+ cap->pchan[i] = chans[i];
+ pchan_map |= (1 << i);
+ }
+ }
+ }
+ if (hdl->sio.mode & SIO_REC) {
+ for (i = 0; i < sizeof(chans) / sizeof(chans[0]); i++) {
+ AUDIO_INITPAR(&ap);
+ ap.pchan = chans[i];
+ if (!sio_oss_adjpar(hdl, &ap))
+ return 0;
+ if (ap.rchan == chans[i]) {
+ cap->rchan[i] = chans[i];
+ rchan_map |= (1 << i);
+ }
+ }
+ }
+
+ /*
+ * fill rates
+ *
+ * rates are not independent from other parameters (eg. on
+ * uaudio devices), so certain rates may not be allowed with
+ * certain encodings. We have to check rates for all encodings
+ */
+ for (j = 0; j < sizeof(encs) / sizeof(encs[0]); j++) {
+ rate_map = 0;
+ if ((enc_map & (1 << j)) == 0)
+ continue;
+ for (i = 0; i < sizeof(rates) / sizeof(rates[0]); i++) {
+ if (sio_oss_testpar(hdl,
+ &cap->enc[j], 0, 0, rates[i])) {
+ cap->rate[i] = rates[i];
+ rate_map |= (1 << i);
+ }
+ }
+ for (conf = 0; conf < nconf; conf++) {
+ if (cap->confs[conf].rate == rate_map) {
+ cap->confs[conf].enc |= (1 << j);
+ break;
+ }
+ }
+ if (conf == nconf) {
+ if (nconf == SIO_NCONF)
+ break;
+ cap->confs[nconf].enc = (1 << j);
+ cap->confs[nconf].pchan = pchan_map;
+ cap->confs[nconf].rchan = rchan_map;
+ cap->confs[nconf].rate = rate_map;
+ nconf++;
+ }
+ }
+ cap->nconf = nconf;
+
+ if (sio_oss_audio_setpar(hdl, &savepar)) {
+ DPERROR("AUDIO_SETPAR");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+sio_oss_getfd(const char *str, unsigned int mode, int nbio)
+{
+ const char *p;
+ char path[DEVPATH_MAX];
+ unsigned int devnum;
+ int fd, flags;
+
+ p = _sndio_parsetype(str, "rsnd");
+ if (p == NULL) {
+ DPRINTF("sio_oss_getfd: %s: \"rsnd\" expected\n", str);
+ return -1;
+ }
+ switch (*p) {
+ case '/':
+ p++;
+ break;
+ default:
+ DPRINTF("sio_oss_getfd: %s: '/' expected\n", str);
+ return -1;
+ }
+ p = _sndio_parsenum(p, &devnum, 255);
+ if (p == NULL || *p != '\0') {
+ DPRINTF("sio_oss_getfd: %s: number expected after '/'\n", str);
+ return -1;
+ }
+ snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
+ if (mode == (SIO_PLAY | SIO_REC))
+ flags = O_RDWR;
+ else
+ flags = (mode & SIO_PLAY) ? O_WRONLY : O_RDONLY;
+ while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) < 0) {
+ if (errno == EINTR)
+ continue;
+ DPERROR(path);
+ return -1;
+ }
+ return fd;
+}
+
+static struct sio_hdl *
+sio_oss_fdopen(int fd, unsigned int mode, int nbio)
+{
+ struct sio_oss_hdl *hdl;
+
+ hdl = malloc(sizeof(struct sio_oss_hdl));
+ if (hdl == NULL)
+ return NULL;
+ _sio_create(&hdl->sio, &sio_oss_ops, mode, nbio);
+
+ /* Set default device parameters */
+ sio_oss_fmt_to_swpar(AFMT_S16_LE, &hdl->swpar);
+ hdl->swpar.msb = 1;
+ hdl->swpar.rate = 44100;
+ hdl->swpar.bps = SIO_BPS(hdl->swpar.bits);
+ hdl->swpar.pchan = hdl->swpar.rchan = 2;
+ hdl->swpar.round = 960; // TODO:
+ hdl->swpar.nblks = 8; // TODO:
+
+ hdl->fd = fd;
+ hdl->filling = 0;
+ return (struct sio_hdl *)hdl;
+}
+
+struct sio_hdl *
+_sio_oss_open(const char *str, unsigned int mode, int nbio)
+{
+ struct sio_hdl *hdl;
+ int fd;
+
+ fd = sio_oss_getfd(str, mode, nbio);
+ if (fd < 0)
+ return NULL;
+ hdl = sio_oss_fdopen(fd, mode, nbio);
+ if (hdl != NULL)
+ return hdl;
+ while (close(fd) < 0 && errno == EINTR)
+ ; /* retry */
+
+ return NULL;
+}
+
+static void
+sio_oss_close(struct sio_hdl *sh)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+
+ while (close(hdl->fd) < 0 && errno == EINTR)
+ ; /* retry */
+ free(hdl);
+}
+
+static int
+sio_oss_start(struct sio_hdl *sh)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+
+ hdl->obpf = hdl->sio.par.pchan * hdl->sio.par.bps;
+ hdl->ibpf = hdl->sio.par.rchan * hdl->sio.par.bps;
+ hdl->ibytes = 0;
+ hdl->obytes = 0;
+ hdl->ierr = 0;
+ hdl->oerr = 0;
+ hdl->idelta = 0;
+ hdl->odelta = 0;
+ hdl->play_pos = 0;
+
+ if (hdl->sio.mode & SIO_PLAY) {
+ /*
+ * keep the device paused and let sio_oss_pollfd() trigger the
+ * start later, to avoid buffer underruns
+ */
+ hdl->filling = 1;
+ } else {
+ /*
+ * no play buffers to fill, start now!
+ */
+ if (sio_oss_audio_start(hdl) < 0) {
+ DPERROR("AUDIO_START");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ _sio_onmove_cb(&hdl->sio, 0);
+ }
+ return 1;
+}
+
+static int
+sio_oss_stop(struct sio_hdl *sh)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+
+ if (hdl->filling) {
+ hdl->filling = 0;
+ return 1;
+ }
+ if (sio_oss_audio_stop(hdl, hdl->fd) < 0) {
+ DPERROR("AUDIO_STOP");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+sio_oss_setpar(struct sio_hdl *sh, struct sio_par *par)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+ struct audio_swpar ap;
+
+ AUDIO_INITPAR(&ap);
+ ap.sig = par->sig;
+ ap.le = par->le;
+ ap.bits = par->bits;
+ ap.bps = par->bps;
+ ap.msb = par->msb;
+ ap.rate = par->rate;
+ if (hdl->sio.mode & SIO_PLAY)
+ ap.pchan = par->pchan;
+ if (hdl->sio.mode & SIO_REC)
+ ap.rchan = par->rchan;
+ if (par->round != ~0U && par->appbufsz != ~0U) {
+ ap.round = par->round;
+ ap.nblks = par->appbufsz / par->round;
+ } else if (par->round != ~0U) {
+ ap.round = par->round;
+ ap.nblks = 2;
+ } else if (par->appbufsz != ~0U) {
+ ap.round = par->appbufsz / 2;
+ ap.nblks = 2;
+ }
+ if (sio_oss_audio_setpar(hdl, &ap) < 0) {
+ DPERROR("AUDIO_SETPAR");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static int
+sio_oss_getpar(struct sio_hdl *sh, struct sio_par *par)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+ struct audio_swpar ap;
+
+ if (sio_oss_audio_getpar(hdl, &ap) < 0) {
+ DPERROR("AUDIO_GETPAR");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ par->sig = ap.sig;
+ par->le = ap.le;
+ par->bits = ap.bits;
+ par->bps = ap.bps;
+ par->msb = ap.msb;
+ par->rate = ap.rate;
+ par->pchan = ap.pchan;
+ par->rchan = ap.rchan;
+ par->round = ap.round;
+ par->appbufsz = par->bufsz = ap.nblks * ap.round;
+ par->xrun = SIO_IGNORE;
+ return 1;
+}
+
+static size_t
+sio_oss_read(struct sio_hdl *sh, void *buf, size_t len)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+ ssize_t n;
+
+ while ((n = read(hdl->fd, buf, len)) < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno != EAGAIN) {
+ DPERROR("sio_oss_read: read");
+ hdl->sio.eof = 1;
+ }
+ return 0;
+ }
+ if (n == 0) {
+ DPRINTF("sio_oss_read: eof\n");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ return n;
+}
+
+static size_t
+sio_oss_write(struct sio_hdl *sh, const void *buf, size_t len)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+ const unsigned char *data = buf;
+ ssize_t n, todo;
+
+ todo = len;
+ while ((n = write(hdl->fd, data, todo)) < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno != EAGAIN) {
+ DPERROR("sio_oss_write: write");
+ hdl->sio.eof = 1;
+ }
+ return 0;
+ }
+
+ hdl->play_pos += n;
+
+ return n;
+}
+
+static int
+sio_oss_nfds(struct sio_hdl *hdl)
+{
+ return 1;
+}
+
+static int
+sio_oss_pollfd(struct sio_hdl *sh, struct pollfd *pfd, int events)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+
+ pfd->fd = hdl->fd;
+ pfd->events = events;
+ if (hdl->filling && hdl->sio.wused == hdl->sio.par.bufsz *
+ hdl->sio.par.pchan * hdl->sio.par.bps) {
+ hdl->filling = 0;
+ if (sio_oss_audio_start(hdl) < 0) {
+ DPERROR("AUDIO_START");
+ hdl->sio.eof = 1;
+ return 0;
+ }
+ _sio_onmove_cb(&hdl->sio, 0);
+ }
+ return 1;
+}
+
+int
+sio_oss_revents(struct sio_hdl *sh, struct pollfd *pfd)
+{
+ struct sio_oss_hdl *hdl = (struct sio_oss_hdl *)sh;
+ struct audio_pos ap;
+ int dierr = 0, doerr = 0, offset, delta;
+ int revents = pfd->revents;
+
+ if ((pfd->revents & POLLHUP) ||
+ (pfd->revents & (POLLIN | POLLOUT)) == 0)
+ return pfd->revents;
+ if (sio_oss_audio_getpos(hdl, &ap) < 0) {
+ DPERROR("sio_oss_revents: GETPOS");
+ hdl->sio.eof = 1;
+ return POLLHUP;
+ }
+ if (hdl->sio.mode & SIO_PLAY) {
+ delta = (ap.play_pos - hdl->obytes) / hdl->obpf;
+ doerr = (ap.play_xrun - hdl->oerr) / hdl->obpf;
+ hdl->obytes = ap.play_pos;
+ hdl->oerr = ap.play_xrun;
+ hdl->odelta += delta;
+ if (!(hdl->sio.mode & SIO_REC)) {
+ hdl->idelta += delta;
+ dierr = doerr;
+ }
+ if (doerr > 0)
+ DPRINTFN(2, "play xrun %d\n", doerr);
+ }
+ if (hdl->sio.mode & SIO_REC) {
+ delta = (ap.rec_pos - hdl->ibytes) / hdl->ibpf;
+ dierr = (ap.rec_xrun - hdl->ierr) / hdl->ibpf;
+ hdl->ibytes = ap.rec_pos;
+ hdl->ierr = ap.rec_xrun;
+ hdl->idelta += delta;
+ if (!(hdl->sio.mode & SIO_PLAY)) {
+ hdl->odelta += delta;
+ doerr = dierr;
+ }
+ if (dierr > 0)
+ DPRINTFN(2, "rec xrun %d\n", dierr);
+ }
+
+ /*
+ * GETPOS reports positions including xruns,
+ * so we have to substract to get the real position
+ */
+ hdl->idelta -= dierr;
+ hdl->odelta -= doerr;
+
+ offset = doerr - dierr;
+ if (offset > 0) {
+ hdl->sio.rdrop += offset * hdl->ibpf;
+ hdl->idelta -= offset;
+ DPRINTFN(2, "will drop %d and pause %d\n", offset, doerr);
+ } else if (offset < 0) {
+ hdl->sio.wsil += -offset * hdl->obpf;
+ hdl->odelta -= -offset;
+ DPRINTFN(2, "will insert %d and pause %d\n", -offset, dierr);
+ }
+
+ delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
+ if (delta > 0) {
+ _sio_onmove_cb(&hdl->sio, delta);
+ hdl->idelta -= delta;
+ hdl->odelta -= delta;
+ }
+ return revents;
+}
+
+static int
+sio_oss_audio_getpos(struct sio_oss_hdl *hdl, struct audio_pos *ap)
+{
+ count_info cio, cii;
+ oss_count_t optr;
+
+ ap->play_pos = hdl->play_pos / hdl->sio.par.bps;
+ ap->play_xrun = 0;
+
+ if (ioctl(hdl->fd, SNDCTL_DSP_GETIPTR, &cii) < 0) {
+ DPERROR("sio_oss_getpos: GETIPTR");
+ return -1;
+ }
+
+ ap->rec_pos = cii.bytes;
+ ap->rec_xrun = 0;
+
+ return 0;
+}
+
+static void
+sio_oss_fmt_to_swpar(int fmt, struct audio_swpar *ap) {
+ switch(fmt) {
+ case AFMT_S8:
+ ap->le = 1;
+ ap->sig = 1;
+ ap->bits = 8;
+ break;
+ case AFMT_U8:
+ ap->le = 1;
+ ap->sig = 0;
+ ap->bits = 8;
+ break;
+ case AFMT_S16_LE:
+ ap->le = 1;
+ ap->sig = 1;
+ ap->bits = 16;
+ break;
+ case AFMT_S16_BE:
+ ap->le = 0;
+ ap->sig = 1;
+ ap->bits = 16;
+ break;
+ case AFMT_U16_LE:
+ ap->le = 1;
+ ap->sig = 0;
+ ap->bits = 16;
+ break;
+ case AFMT_U16_BE:
+ ap->le = 0;
+ ap->sig = 0;
+ ap->bits = 16;
+ break;
+ case AFMT_S24_LE:
+ ap->le = 1;
+ ap->sig = 1;
+ ap->bits = 24;
+ break;
+ case AFMT_S24_BE:
+ ap->le = 0;
+ ap->sig = 1;
+ ap->bits = 24;
+ break;
+ case AFMT_U24_LE:
+ ap->le = 1;
+ ap->sig = 0;
+ ap->bits = 24;
+ break;
+ case AFMT_U24_BE:
+ ap->le = 0;
+ ap->sig = 0;
+ ap->bits = 24;
+ break;
+ case AFMT_S32_LE:
+ ap->le = 1;
+ ap->sig = 1;
+ ap->bits = 32;
+ break;
+ case AFMT_S32_BE:
+ ap->le = 0;
+ ap->sig = 1;
+ ap->bits = 32;
+ break;
+ case AFMT_U32_LE:
+ ap->le = 1;
+ ap->sig = 0;
+ ap->bits = 32;
+ break;
+ case AFMT_U32_BE:
+ ap->le = 0;
+ ap->sig = 0;
+ ap->bits = 32;
+ break;
+ }
+}
+
+static int
+sio_oss_swpar_to_fmt(struct audio_swpar *ap)
+{
+ unsigned int bits = ap->bits;
+ unsigned int sig = ap->sig;
+ unsigned int le = ap->le;
+
+ switch(bits) {
+ case 8:
+ if (sig)
+ return AFMT_S8;
+ else
+ return AFMT_U8;
+ break;
+ case 16:
+ if (sig)
+ if (le)
+ return AFMT_S16_LE;
+ else
+ return AFMT_S16_BE;
+ else
+ if (le)
+ return AFMT_U16_LE;
+ else
+ return AFMT_U16_BE;
+ break;
+ break;
+ case 24:
+ if (sig)
+ if (le)
+ return AFMT_S24_LE;
+ else
+ return AFMT_S24_BE;
+ else
+ if (le)
+ return AFMT_U24_LE;
+ else
+ return AFMT_U24_BE;
+ break;
+ break;
+ case 32:
+ if (sig)
+ if (le)
+ return AFMT_S32_LE;
+ else
+ return AFMT_S32_BE;
+ else
+ if (le)
+ return AFMT_U32_LE;
+ else
+ return AFMT_U32_BE;
+ break;
+ default:
+ if (sig)
+ if (SIO_LE_NATIVE)
+ return AFMT_S16_LE;
+ else
+ return AFMT_S16_BE;
+ else
+ if (SIO_LE_NATIVE)
+ return AFMT_U16_LE;
+ else
+ return AFMT_U16_BE;
+ }
+}
+
+static int sio_oss_audio_getpar(struct sio_oss_hdl *hdl, struct audio_swpar *ap)
+{
+ audio_buf_info bi;
+
+ *ap = hdl->swpar;
+
+ return 0;
+}
+
+static int sio_oss_audio_setpar(struct sio_oss_hdl *hdl, struct audio_swpar *ap)
+{
+ int fmt = sio_oss_swpar_to_fmt(ap);
+ if (fmt < 0)
+ return -1;
+
+ if (ioctl(hdl->fd, SNDCTL_DSP_SETFMT, &fmt) < 0)
+ return -1;
+
+ sio_oss_fmt_to_swpar(fmt, ap);
+
+ if (ioctl(hdl->fd, SNDCTL_DSP_SPEED, &ap->rate) < 0)
+ return -1;
+
+ ap->bps = SIO_BPS(ap->bits);
+ ap->msb = 0;
+
+ int chan = (hdl->sio.mode & SIO_PLAY) ? ap->pchan : ap->rchan;
+ if (ioctl(hdl->fd, SNDCTL_DSP_CHANNELS, &chan) < 0)
+ return -1;
+
+ ap->pchan = ap->rchan = chan;
+
+ hdl->swpar = *ap;
+
+ return 0;
+}
+
+static int sio_oss_audio_start(struct sio_oss_hdl *hdl) {
+ // Empty playback buffer
+ if (ioctl(hdl->fd, SNDCTL_DSP_SKIP, NULL) < 0) {
+ DPERROR("SNDCTL_DSP_SKIP");
+ return -1;
+ }
+
+ int trigger;
+
+ if (hdl->sio.mode & SIO_PLAY) {
+ trigger = PCM_ENABLE_OUTPUT;
+ }
+ if (hdl->sio.mode & SIO_REC) {
+ trigger = PCM_ENABLE_INPUT;
+ } // TODO:
+
+ if (ioctl(hdl->fd, SNDCTL_DSP_SETTRIGGER, &trigger)) {
+ DPERROR("sio_oss_start: SETTRIGGER");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sio_oss_audio_stop(struct sio_oss_hdl *hdl, int fd) {
+ /* Block until buffer is played */
+ if (ioctl(hdl->fd, SNDCTL_DSP_SYNC, NULL) < 0) {
+ return -1;
+ }
+
+ // TODO: Check mode and use HALT_{IN,OUT}PUT
+ if (ioctl(hdl->fd, SNDCTL_DSP_HALT, NULL) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* defined USE_OSS */

View File

@ -0,0 +1,12 @@
--- libsndio/sio_priv.h.orig 2015-01-17 23:09:04 UTC
+++ libsndio/sio_priv.h
@@ -69,6 +69,9 @@ struct sio_hdl *_sio_aucat_open(const ch
#ifdef USE_SUN
struct sio_hdl *_sio_sun_open(const char *, unsigned, int);
#endif
+#ifdef USE_OSS
+struct sio_hdl *_sio_oss_open(const char *, unsigned, int);
+#endif
#ifdef USE_ALSA
struct sio_hdl *_sio_alsa_open(const char *, unsigned, int);
#endif

View File

@ -0,0 +1,32 @@
#!/bin/sh
#
# $FreeBSD$
#
# PROVIDE: sndiod
# REQUIRE: NETWORKING sysctl
# BEFORE: DAEMON
# KEYWORD: shutdown
# By default sndiod will use the audio device from
# hw.snd.default_unit. You can override this by setting sndiod_flags.
#
# To connect to a remote sndiod use e.g.
# sndiod_flags="-f snd@remotehost/0"
#
# To use /dev/dsp5
# sndiod_flags="-f rsnd/5"
. /etc/rc.subr
name=sndiod
rcvar=sndiod_enable
load_rc_config $name
_sndiod_devnum=$($SYSCTL -n hw.snd.default_unit)
: ${sndiod_enable="NO"}
: ${sndiod_flags="-f rsnd/$_sndiod_devnum"}
command="%%PREFIX%%/bin/sndiod"
run_rc_command "$1"

10
audio/sndio/pkg-descr Normal file
View File

@ -0,0 +1,10 @@
Sndio is a small audio and MIDI framework part of the OpenBSD project.
It provides an lightweight audio & MIDI server and a fully documented
user-space API to access either the server or directly the hardware in
a uniform way. Sndio is designed to work for desktop applications,
but pays special attention to synchronization mechanisms and
reliability required by music applications. Reliability through
simplicity are part of the project goals.
WWW: http://www.sndio.org/

29
audio/sndio/pkg-message Normal file
View File

@ -0,0 +1,29 @@
sndio's OSS support (i.e. local playback support) is highly
experimental. If you run into problems please file a bug at
https://github.com/t6/sndio or send an email to t+sndio@tobik.me.
The recommended way to use this port is to have a remote sndio server
running on a Linux or OpenBSD host.
If you want clients to auto-play to your remote sndio server, enable
sndiod with:
sysrc sndiod_enable=YES sndiod_flags="-f snd@remotehost/0"
service sndiod start
For local playback simply enabling the sndiod server will suffice
sysrc sndiod_enable=YES
Alternatively set the AUDIODEVICE environment variable so clients know
where to stream to
export AUDIODEVICE=snd@remotehost/0
There is no sndio support in the official FreeBSD ports tree yet. The
fork at https://github.com/t6/freebsd-port-sndio contains patches that
enable sndio support in important ports.
audio/pulseaudio-module-sndio is a PulseAudio module that allows you
to play to your sndio server. This is useful for ports that have
PulseAudio support but no direct sndio support.

35
audio/sndio/pkg-plist Normal file
View File

@ -0,0 +1,35 @@
bin/aucat
bin/midicat
bin/sndiod
include/sndio.h
lib/libsndio.so
lib/libsndio.so.6.1
man/man1/aucat.1.gz
man/man1/midicat.1.gz
man/man3/mio_close.3.gz
man/man3/mio_eof.3.gz
man/man3/mio_nfds.3.gz
man/man3/mio_open.3.gz
man/man3/mio_pollfd.3.gz
man/man3/mio_read.3.gz
man/man3/mio_revents.3.gz
man/man3/mio_write.3.gz
man/man3/sio_close.3.gz
man/man3/sio_eof.3.gz
man/man3/sio_getcap.3.gz
man/man3/sio_getpar.3.gz
man/man3/sio_initpar.3.gz
man/man3/sio_nfds.3.gz
man/man3/sio_onmove.3.gz
man/man3/sio_onvol.3.gz
man/man3/sio_open.3.gz
man/man3/sio_pollfd.3.gz
man/man3/sio_read.3.gz
man/man3/sio_revents.3.gz
man/man3/sio_setpar.3.gz
man/man3/sio_setvol.3.gz
man/man3/sio_start.3.gz
man/man3/sio_stop.3.gz
man/man3/sio_write.3.gz
man/man7/sndio.7.gz
man/man8/sndiod.8.gz