aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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