aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKurt Jaeger <pi@FreeBSD.org>2016-08-03 06:15:41 +0000
committerKurt Jaeger <pi@FreeBSD.org>2016-08-03 06:15:41 +0000
commit026baf8a419918d86c3559316e886ff77c8eb0a1 (patch)
treef9d9753690b2c86cea7e3547302f66dde94a755f
parent7f5840ad268a51ec2fd52677cad792f7a99debc9 (diff)
downloadports-026baf8a419918d86c3559316e886ff77c8eb0a1.tar.gz
ports-026baf8a419918d86c3559316e886ff77c8eb0a1.zip
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>
Notes
Notes: svn path=/head/; revision=419497
-rw-r--r--GIDs1
-rw-r--r--UIDs1
-rw-r--r--audio/Makefile1
-rw-r--r--audio/sndio/Makefile41
-rw-r--r--audio/sndio/distinfo3
-rw-r--r--audio/sndio/files/patch-configure60
-rw-r--r--audio/sndio/files/patch-libsndio_Makefile.in17
-rw-r--r--audio/sndio/files/patch-libsndio_sio.c20
-rw-r--r--audio/sndio/files/patch-libsndio_sio__oss.c893
-rw-r--r--audio/sndio/files/patch-libsndio_sio__priv.h12
-rw-r--r--audio/sndio/files/sndiod.in32
-rw-r--r--audio/sndio/pkg-descr10
-rw-r--r--audio/sndio/pkg-message29
-rw-r--r--audio/sndio/pkg-plist35
14 files changed, 1155 insertions, 0 deletions
diff --git a/GIDs b/GIDs
index 8d5b13e16dda..dcdb24cbd749 100644
--- a/GIDs
+++ b/GIDs
@@ -234,6 +234,7 @@ aox:*:666:
riak:*:667:
bnetd:*:700:
fastnetmon:*:701:
+_sndio:*:702:
bopm:*:717:
openxpki:*:777:
zetacoin:*:780:
diff --git a/UIDs b/UIDs
index 5efcccc7b4a5..71e45cf8c57c 100644
--- a/UIDs
+++ b/UIDs
@@ -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
diff --git a/audio/Makefile b/audio/Makefile
index 822d8d57c57b..00b5e7a61a24 100644
--- a/audio/Makefile
+++ b/audio/Makefile
@@ -697,6 +697,7 @@
SUBDIR += smasher
SUBDIR += snack
SUBDIR += snd
+ SUBDIR += sndio
SUBDIR += solfege
SUBDIR += sonata
SUBDIR += sooperlooper
diff --git a/audio/sndio/Makefile b/audio/sndio/Makefile
new file mode 100644
index 000000000000..f11b150dc23c
--- /dev/null
+++ b/audio/sndio/Makefile
@@ -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>
diff --git a/audio/sndio/distinfo b/audio/sndio/distinfo
new file mode 100644
index 000000000000..4293504038ac
--- /dev/null
+++ b/audio/sndio/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1465315037
+SHA256 (sndio-1.1.0.tar.gz) = fcd7f845ff70f38c2898d737450b8aa3e1bb0afb9d147e8429ef22c0b2c2db57
+SIZE (sndio-1.1.0.tar.gz) = 121018
diff --git a/audio/sndio/files/patch-configure b/audio/sndio/files/patch-configure
new file mode 100644
index 000000000000..9b967a11bf0b
--- /dev/null
+++ b/audio/sndio/files/patch-configure
@@ -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
+
diff --git a/audio/sndio/files/patch-libsndio_Makefile.in b/audio/sndio/files/patch-libsndio_Makefile.in
new file mode 100644
index 000000000000..e199ca7596b6
--- /dev/null
+++ b/audio/sndio/files/patch-libsndio_Makefile.in
@@ -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
diff --git a/audio/sndio/files/patch-libsndio_sio.c b/audio/sndio/files/patch-libsndio_sio.c
new file mode 100644
index 000000000000..44de62ae4ae5
--- /dev/null
+++ b/audio/sndio/files/patch-libsndio_sio.c
@@ -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
diff --git a/audio/sndio/files/patch-libsndio_sio__oss.c b/audio/sndio/files/patch-libsndio_sio__oss.c
new file mode 100644
index 000000000000..24b9f0481980
--- /dev/null
+++ b/audio/sndio/files/patch-libsndio_sio__oss.c
@@ -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 */
diff --git a/audio/sndio/files/patch-libsndio_sio__priv.h b/audio/sndio/files/patch-libsndio_sio__priv.h
new file mode 100644
index 000000000000..d8cb0fc3734e
--- /dev/null
+++ b/audio/sndio/files/patch-libsndio_sio__priv.h
@@ -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
diff --git a/audio/sndio/files/sndiod.in b/audio/sndio/files/sndiod.in
new file mode 100644
index 000000000000..87814017d3a7
--- /dev/null
+++ b/audio/sndio/files/sndiod.in
@@ -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"
diff --git a/audio/sndio/pkg-descr b/audio/sndio/pkg-descr
new file mode 100644
index 000000000000..075b321f0fcb
--- /dev/null
+++ b/audio/sndio/pkg-descr
@@ -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/
diff --git a/audio/sndio/pkg-message b/audio/sndio/pkg-message
new file mode 100644
index 000000000000..9da5476417a0
--- /dev/null
+++ b/audio/sndio/pkg-message
@@ -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.
diff --git a/audio/sndio/pkg-plist b/audio/sndio/pkg-plist
new file mode 100644
index 000000000000..1425a6ddc6de
--- /dev/null
+++ b/audio/sndio/pkg-plist
@@ -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