merge head

This commit is contained in:
Alexandre Ratchov 2016-01-26 10:23:38 +01:00
parent 7b062cf12f
commit 5dc67b663e
44 changed files with 544 additions and 948 deletions

View File

@ -14,7 +14,7 @@ LDADD = -lsndio @ldadd@
@vars@
#
# binaries, documentation, man pages and examples will be installed in
# binaries, documentation, man pages and examples will be installed in
# ${BIN_DIR}, ${MAN1_DIR}
#
BIN_DIR = @bindir@
@ -45,7 +45,7 @@ clean:
OBJS = abuf.o afile.o aucat.o dsp.o utils.o
aucat: ${OBJS}
${CC} ${LDFLAGS} ${LIB} -o aucat ${OBJS} ${LDADD}
${CC} ${LDFLAGS} ${LIB} -o aucat ${OBJS} ${LDADD}
.c.o:
${CC} ${CFLAGS} ${INCLUDE} ${DEFS} -c $<

View File

@ -53,7 +53,7 @@ abuf_init(struct abuf *buf, unsigned int len)
void
abuf_done(struct abuf *buf)
{
#ifdef DEBUG
#ifdef DEBUG
if (buf->used > 0) {
if (log_level >= 3) {
log_puts("deleting non-empty buffer, used = ");

View File

@ -563,7 +563,7 @@ afile_aiff_readhdr(struct afile *f)
log_puts(f->path);
log_puts(": failed to read chunk header\n");
return 0;
}
}
csize = be32_get(&chunk.size);
if (memcmp(chunk.id, aiff_id_comm, 4) == 0) {
if (!afile_aiff_readcomm(f, csize, comp, &nfr))
@ -572,7 +572,8 @@ afile_aiff_readhdr(struct afile *f)
} else if (memcmp(chunk.id, aiff_id_data, 4) == 0) {
if (!afile_aiff_readdata(f, csize, &offs))
return 0;
f->startpos = sizeof(form) + pos + sizeof(chunk) + offs;
f->startpos = sizeof(form) + pos +
sizeof(chunk) + offs;
break;
} else {
#ifdef DEBUG
@ -927,7 +928,8 @@ afile_open(struct afile *f, char *path, int hdr, int flags,
f->fd = STDOUT_FILENO;
} else {
f->path = path;
f->fd = open(f->path, O_WRONLY | O_TRUNC | O_CREAT, 0666);
f->fd = open(f->path,
O_WRONLY | O_TRUNC | O_CREAT, 0666);
if (f->fd < 0) {
log_puts(f->path);
log_puts(": failed to create file\n");
@ -956,7 +958,8 @@ afile_open(struct afile *f, char *path, int hdr, int flags,
f->par.msb = 1;
f->endpos = f->startpos = sizeof(struct aiff_hdr);
f->maxpos = 0x7fffffff;
if (!afile_writehdr(f, &dummy, sizeof(struct aiff_hdr)))
if (!afile_writehdr(f, &dummy,
sizeof(struct aiff_hdr)))
goto bad_close;
} else if (f->hdr == AFILE_HDR_AU) {
f->par.bits = (f->par.bits + 7) & ~7;

View File

@ -87,7 +87,7 @@ Increase log verbosity.
Encoding of the audio file.
The default is
.Va s16 .
Encoding names use the follwing scheme: signedness
Encoding names use the following scheme: signedness
.Po
.Va s
or
@ -223,7 +223,7 @@ back to the starting position.
.El
.Pp
MIDI control is intended to be used together with
.Xr sndiod 1 .
.Xr sndiod 8 .
For instance, the following command will create two devices:
the default
.Va snd/0
@ -280,9 +280,9 @@ $ aucat -n -i stereo.wav -c 0:0 -o left.wav \e
.Xr audioctl 1 ,
.Xr cdio 1 ,
.Xr mixerctl 1 ,
.Xr sndiod 1 ,
.Xr audio 4 ,
.Xr sndio 7
.Xr sndio 7 ,
.Xr sndiod 8
.Sh BUGS
Resampling is low quality.
.Pp

View File

@ -308,7 +308,8 @@ slot_init(struct slot *s)
s->cmin, s->cmax,
0, dev_pchan - 1,
0, dev_pchan - 1);
if (s->afile.fmt != AFILE_FMT_PCM || !aparams_native(&s->afile.par)) {
if (s->afile.fmt != AFILE_FMT_PCM ||
!aparams_native(&s->afile.par)) {
dec_init(&s->conv, &s->afile.par, slot_nch);
s->convbuf =
xmalloc(s->round * slot_nch * sizeof(adata_t));
@ -425,7 +426,7 @@ slot_del(struct slot *s)
xfree(s);
}
static int
static int
play_filt_resamp(struct slot *s, void *res_in, void *out, int todo)
{
int i, offs, vol, nch;
@ -455,7 +456,7 @@ play_filt_resamp(struct slot *s, void *res_in, void *out, int todo)
return todo;
}
static int
static int
play_filt_dec(struct slot *s, void *in, void *out, int todo)
{
void *tmp;
@ -506,7 +507,7 @@ slot_mix_badd(struct slot *s, adata_t *odata)
return done;
}
static int
static int
rec_filt_resamp(struct slot *s, void *in, void *res_out, int todo)
{
int i, vol, offs, nch;
@ -535,7 +536,7 @@ rec_filt_resamp(struct slot *s, void *in, void *res_out, int todo)
return todo;
}
static int
static int
rec_filt_enc(struct slot *s, void *in, void *out, int todo)
{
void *tmp;
@ -1032,7 +1033,8 @@ playrec_cycle(void)
n = sio_read(dev_sh, p, todo);
if (n == 0) {
log_puts(dev_name);
log_puts(": failed to read from device\n");
log_puts(": failed to read "
"from device\n");
return 0;
}
p += n;
@ -1114,7 +1116,7 @@ playrec(char *dev, int mode, int bufsz, char *port)
continue;
log_puts("poll failed\n");
panic();
}
}
if (dev_pstate == DEV_START) {
ev = sio_revents(dev_sh, pfds);
if (ev & POLLHUP) {
@ -1276,7 +1278,7 @@ main(int argc, char **argv)
port = NULL;
dev = NULL;
mode = 0;
while ((c = getopt(argc, argv, "b:c:de:f:h:i:j:no:q:r:t:v:")) != -1) {
switch (c) {
case 'b':
@ -1359,7 +1361,7 @@ main(int argc, char **argv)
if (mode == 0) {
log_puts("at least -i or -o required\n");
return 1;
}
}
if (!playrec(dev, mode, bufsz, port))
return 1;
}

View File

@ -222,7 +222,7 @@ aparams_strtoenc(struct aparams *par, char *istr)
return 0;
done:
par->msb = msb;
par->msb = msb;
par->sig = sig;
par->bits = bits;
par->bps = bps;
@ -366,7 +366,8 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo)
* initialize resampler with ibufsz/obufsz factor and "nch" channels
*/
void
resamp_init(struct resamp *p, unsigned int iblksz, unsigned int oblksz, int nch)
resamp_init(struct resamp *p, unsigned int iblksz,
unsigned int oblksz, int nch)
{
unsigned int i;
@ -516,7 +517,7 @@ enc_init(struct conv *p, struct aparams *par, int nch)
p->bias = (1U << 31) >> p->shift;
} else {
p->bias = 0;
}
}
if (!par->le) {
p->bfirst = par->bps - 1;
p->bnext = -1;
@ -673,7 +674,8 @@ dec_do_float(struct conv *p, unsigned char *in, unsigned char *out, int todo)
* convert samples from ulaw/alaw to adata_t
*/
void
dec_do_ulaw(struct conv *p, unsigned char *in, unsigned char *out, int todo, int is_alaw)
dec_do_ulaw(struct conv *p, unsigned char *in,
unsigned char *out, int todo, int is_alaw)
{
unsigned int f;
unsigned char *idata;
@ -711,7 +713,7 @@ dec_init(struct conv *p, struct aparams *par, int nch)
p->bias = (1U << 31) >> p->shift;
} else {
p->bias = 0;
}
}
if (par->le) {
p->bfirst = par->bps - 1;
p->bnext = -1;

View File

@ -91,7 +91,7 @@ log_putx(unsigned long num)
c += (c < 10) ? '0' : 'a' - 10;
LOG_PUTC(c);
}
} else
} else
LOG_PUTC('0');
}

8
configure vendored
View File

@ -59,6 +59,14 @@ case `uname` in
so="$so libsndio.so"
defs='-D_GNU_SOURCE -DDEV_RANDOM=\\"/dev/urandom\\"'
;;
NetBSD)
sun=no
rmidi=yes
user=_sndio
so="$so libsndio.so"
defs='-DHAVE_ARC4RANDOM -DHAVE_ISSETUGID \\\
-DHAVE_STRLCAT -DHAVE_STRLCPY'
;;
OpenBSD)
sun=yes
rmidi=yes

View File

@ -19,7 +19,7 @@
#include <stdint.h>
/*
/*
* unix-domain socket name is:
*
* DIR [ '-' UID ] '/' FILE UNIT

View File

@ -468,15 +468,24 @@ parsestr(const char *str, char *rstr, unsigned int max)
}
int
_aucat_open(struct aucat *hdl, const char *str, unsigned int mode,
unsigned int type)
_aucat_open(struct aucat *hdl, const char *str, unsigned int mode)
{
extern char *__progname;
int eof;
char host[NI_MAXHOST], opt[AMSG_OPTMAX];
const char *p = str;
unsigned int unit, devnum;
const char *p;
unsigned int unit, devnum, type;
if ((p = _sndio_parsetype(str, "snd")) != NULL)
type = 0;
else if ((p = _sndio_parsetype(str, "midithru")) != NULL)
type = 1;
else if ((p = _sndio_parsetype(str, "midi")) != NULL)
type = 2;
else {
DPRINTF("%s: unsupported device type\n", str);
return -1;
}
if (*p == '@') {
p = parsestr(++p, host, NI_MAXHOST);
if (p == NULL)
@ -489,7 +498,7 @@ _aucat_open(struct aucat *hdl, const char *str, unsigned int mode,
return 0;
} else
unit = 0;
if (*p != '/' && *p != ':') {
if (*p != '/') {
DPRINTF("%s: '/' expected\n", str);
return 0;
}

View File

@ -21,7 +21,7 @@ int _aucat_rmsg(struct aucat *, int *);
int _aucat_wmsg(struct aucat *, int *);
size_t _aucat_rdata(struct aucat *, void *, size_t, int *);
size_t _aucat_wdata(struct aucat *, const void *, size_t, unsigned, int *);
int _aucat_open(struct aucat *, const char *, unsigned, unsigned);
int _aucat_open(struct aucat *, const char *, unsigned);
void _aucat_close(struct aucat *, int);
int _aucat_pollfd(struct aucat *, struct pollfd *, int);
int _aucat_revents(struct aucat *, struct pollfd *);

View File

@ -36,7 +36,6 @@ mio_open(const char *str, unsigned int mode, int nbio)
{
static char portany[] = MIO_PORTANY;
struct mio_hdl *hdl;
const char *p;
#ifdef DEBUG
_sndio_debug_init();
@ -51,32 +50,28 @@ mio_open(const char *str, unsigned int mode, int nbio)
str = portany;
}
if (strcmp(str, portany) == 0) {
hdl = _mio_aucat_open("/0", mode, nbio, 1);
hdl = _mio_aucat_open("midithru/0", mode, nbio);
if (hdl != NULL)
return hdl;
#if defined(USE_RMIDI)
return _mio_rmidi_open("/0", mode, nbio);
return _mio_rmidi_open("rmidi/0", mode, nbio);
#elif defined(USE_ALSA)
return mio_alsa_open("/0", mode, nbio);
return _mio_alsa_open("rmidi/0", mode, nbio);
#else
return NULL;
#endif
}
if ((p = _sndio_parsetype(str, "snd")) != NULL ||
(p = _sndio_parsetype(str, "aucat")) != NULL)
return _mio_aucat_open(p, mode, nbio, 0);
if ((p = _sndio_parsetype(str, "midithru")) != NULL)
return _mio_aucat_open(p, mode, nbio, 1);
if ((p = _sndio_parsetype(str, "midi")) != NULL)
return _mio_aucat_open(p, mode, nbio, 2);
#if defined(USE_RMIDI) || defined(USE_ALSA)
if ((p = _sndio_parsetype(str, "rmidi")) != NULL) {
#if defined(USE_SUN)
return _mio_rmidi_open(p, mode, nbio);
if (_sndio_parsetype(str, "snd") ||
_sndio_parsetype(str, "midithru") ||
_sndio_parsetype(str, "midi"))
return _mio_aucat_open(str, mode, nbio);
if (_sndio_parsetype(str, "rmidi"))
#if defined(USE_RMIDI)
return _mio_rmidi_open(str, mode, nbio);
#elif defined(USE_ALSA)
return mio_alsa_open(p, mode, nbio);
#endif
}
return _mio_alsa_open(str, mode, nbio);
#else
return NULL;
#endif
DPRINTF("mio_open: %s: unknown device type\n", str);
return NULL;

View File

@ -35,7 +35,7 @@
#ifdef DEBUG
static snd_output_t *output = NULL;
#define DALSA(str, err) fprintf(stderr, "%s: %s\n", str, snd_strerror(err))
#define DALSA(str, err) fprintf(stderr, "%s: %s\n", str, snd_strerror(err))
#else
#define DALSA(str, err) do {} while (0)
#endif
@ -64,18 +64,24 @@ static struct mio_ops mio_alsa_ops = {
};
struct mio_hdl *
mio_alsa_open(const char *str, unsigned int mode, int nbio)
_mio_alsa_open(const char *_str, unsigned int mode, int nbio)
{
const char *p;
struct mio_alsa_hdl *hdl;
size_t len;
int rc;
switch (*str) {
p = _sndio_parsetype(_str, "rmidi");
if (p == NULL) {
DPRINTF("_mio_alsa_open: %s: \"rsnd\" expected\n", _str);
return NULL;
}
switch (*p) {
case '/':
str++;
p++;
break;
default:
DPRINTF("mio_alsa_open: %s: '/<devnum>' expected\n", str);
DPRINTF("_mio_alsa_open: %s: '/' expected\n", _str);
return NULL;
}
hdl = malloc(sizeof(struct mio_alsa_hdl));
@ -87,14 +93,14 @@ mio_alsa_open(const char *str, unsigned int mode, int nbio)
if (rc < 0)
DALSA("couldn't attach to stderr", rc);
#endif
len = strlen(str);
len = strlen(p);
hdl->devname = malloc(len + sizeof(DEVNAME_PREFIX));
if (hdl->devname == NULL) {
free(hdl);
return NULL;
}
memcpy(hdl->devname, DEVNAME_PREFIX, sizeof(DEVNAME_PREFIX) - 1);
memcpy(hdl->devname + sizeof(DEVNAME_PREFIX) - 1, str, len + 1);
memcpy(hdl->devname + sizeof(DEVNAME_PREFIX) - 1, p, len + 1);
hdl->in = hdl->out = NULL;
rc = snd_rawmidi_open(&hdl->in, &hdl->out,
hdl->devname, SND_RAWMIDI_NONBLOCK);

View File

@ -75,7 +75,8 @@ mio_aucat_runmsg(struct mio_aucat_hdl *hdl)
delta, hdl->aucat.maxwrite);
break;
default:
DPRINTF("mio_aucat_runmsg: unhandled message %u\n", hdl->aucat.rmsg.cmd);
DPRINTF("mio_aucat_runmsg: unhandled message %u\n",
hdl->aucat.rmsg.cmd);
hdl->mio.eof = 1;
return 0;
}
@ -85,15 +86,14 @@ mio_aucat_runmsg(struct mio_aucat_hdl *hdl)
}
struct mio_hdl *
_mio_aucat_open(const char *str, unsigned int mode,
int nbio, unsigned int type)
_mio_aucat_open(const char *str, unsigned int mode, int nbio)
{
struct mio_aucat_hdl *hdl;
hdl = malloc(sizeof(struct mio_aucat_hdl));
if (hdl == NULL)
return NULL;
if (!_aucat_open(&hdl->aucat, str, mode, type))
if (!_aucat_open(&hdl->aucat, str, mode))
goto bad;
_mio_create(&hdl->mio, &mio_aucat_ops, mode, nbio);
if (!_aucat_setfl(&hdl->aucat, 1, &hdl->mio.eof))

View File

@ -51,7 +51,7 @@ The
library allows user processes to access
.Xr midi 4
hardware and
.Xr sndiod 1
.Xr sndiod 8
MIDI thru boxes and control ports in a uniform way.
.Ss Opening and closing an MIDI stream
First the application must call the
@ -243,7 +243,7 @@ The debug level:
may be a value between 0 and 2.
.El
.Sh SEE ALSO
.Xr sndiod 1 ,
.Xr poll 2 ,
.Xr midi 4 ,
.Xr sndio 7
.Xr sndio 7 ,
.Xr sndiod 8

View File

@ -45,9 +45,9 @@ struct mio_ops {
struct mio_hdl *_mio_rmidi_open(const char *, unsigned, int);
#ifdef USE_ALSA
struct mio_hdl *mio_alsa_open(const char *, unsigned, int);
struct mio_hdl *_mio_alsa_open(const char *, unsigned, int);
#endif
struct mio_hdl *_mio_aucat_open(const char *, unsigned, int, unsigned);
struct mio_hdl *_mio_aucat_open(const char *, unsigned, int);
void _mio_create(struct mio_hdl *, struct mio_ops *, unsigned, int);
void _mio_destroy(struct mio_hdl *);

View File

@ -15,6 +15,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef USE_RMIDI
#include <sys/types.h>
#include <sys/stat.h>
@ -56,33 +57,34 @@ static struct mio_ops mio_rmidi_ops = {
mio_rmidi_revents
};
struct mio_hdl *
_mio_rmidi_open(const char *str, unsigned int mode, int nbio)
static int
mio_rmidi_getfd(const char *str, unsigned int mode, int nbio)
{
int fd, flags;
struct mio_rmidi_hdl *hdl;
const char *p;
char path[DEVPATH_MAX];
unsigned int devnum;
int fd, flags;
switch (*str) {
p = _sndio_parsetype(str, "rmidi");
if (p == NULL) {
DPRINTF("mio_rmidi_getfd: %s: \"rsnd\" expected\n", str);
return -1;
}
switch (*p) {
case '/':
str++;
p++;
break;
default:
DPRINTF("_mio_rmidi_open: %s: '/<devnum>' expected\n", str);
return NULL;
DPRINTF("mio_rmidi_getfd: %s: '/' expected\n", str);
return -1;
}
str = _sndio_parsenum(str, &devnum, 255);
if (str == NULL || *str != '\0') {
DPRINTF("_mio_rmidi_open: can't determine device number\n");
return NULL;
p = _sndio_parsenum(p, &devnum, 255);
if (p == NULL || *p != '\0') {
DPRINTF("mio_rmidi_getfd: %s: number expected after '/'\n", str);
return -1;
}
hdl = malloc(sizeof(struct mio_rmidi_hdl));
if (hdl == NULL)
return NULL;
_mio_create(&hdl->mio, &mio_rmidi_ops, mode, nbio);
snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
if (mode == (MIO_OUT | MIO_IN))
if (mode == (MIO_IN | MIO_OUT))
flags = O_RDWR;
else
flags = (mode & MIO_OUT) ? O_WRONLY : O_RDONLY;
@ -90,12 +92,38 @@ _mio_rmidi_open(const char *str, unsigned int mode, int nbio)
if (errno == EINTR)
continue;
DPERROR(path);
goto bad_free;
return -1;
}
return fd;
}
static struct mio_hdl *
mio_rmidi_fdopen(int fd, unsigned int mode, int nbio)
{
struct mio_rmidi_hdl *hdl;
hdl = malloc(sizeof(struct mio_rmidi_hdl));
if (hdl == NULL)
return NULL;
_mio_create(&hdl->mio, &mio_rmidi_ops, mode, nbio);
hdl->fd = fd;
return (struct mio_hdl *)hdl;
bad_free:
free(hdl);
}
struct mio_hdl *
_mio_rmidi_open(const char *str, unsigned int mode, int nbio)
{
struct mio_hdl *hdl;
int fd;
fd = mio_rmidi_getfd(str, mode, nbio);
if (fd < 0)
return NULL;
hdl = mio_rmidi_fdopen(fd, mode, nbio);
if (hdl != NULL)
return hdl;
while (close(fd) < 0 && errno == EINTR)
; /* retry */
return NULL;
}
@ -147,7 +175,7 @@ mio_rmidi_write(struct mio_hdl *sh, const void *buf, size_t len)
DPERROR("mio_rmidi_write: write");
hdl->mio.eof = 1;
}
return 0;
return 0;
}
return n;
}
@ -173,3 +201,4 @@ mio_rmidi_revents(struct mio_hdl *sh, struct pollfd *pfd)
{
return pfd->revents;
}
#endif /* defined USE_RMIDI */

View File

@ -45,7 +45,6 @@ sio_open(const char *str, unsigned int mode, int nbio)
{
static char devany[] = SIO_DEVANY;
struct sio_hdl *hdl;
const char *p;
#ifdef DEBUG
_sndio_debug_init();
@ -60,29 +59,26 @@ sio_open(const char *str, unsigned int mode, int nbio)
str = devany;
}
if (strcmp(str, devany) == 0) {
hdl = _sio_aucat_open("/0", mode, nbio);
hdl = _sio_aucat_open("snd/0", mode, nbio);
if (hdl != NULL)
return hdl;
#if defined(USE_SUN)
return _sio_sun_open("/0", mode, nbio);
return _sio_sun_open("rsnd/0", mode, nbio);
#elif defined(USE_ALSA)
return _sio_alsa_open("/0", mode, nbio);
return _sio_alsa_open("rsnd/0", mode, nbio);
#else
return NULL;
#endif
}
if ((p = _sndio_parsetype(str, "snd")) != NULL ||
(p = _sndio_parsetype(str, "aucat")) != NULL)
return _sio_aucat_open(p, mode, nbio);
#if defined(USE_ALSA) || defined(USE_SUN)
if ((p = _sndio_parsetype(str, "rsnd")) != NULL ||
(p = _sndio_parsetype(str, "sun")) != NULL) {
if (_sndio_parsetype(str, "snd"))
return _sio_aucat_open(str, mode, nbio);
if (_sndio_parsetype(str, "rsnd"))
#if defined(USE_SUN)
return _sio_sun_open(p, mode, nbio);
return _sio_sun_open(str, mode, nbio);
#elif defined(USE_ALSA)
return _sio_alsa_open(p, mode, nbio);
#endif
}
return _sio_alsa_open(str, mode, nbio);
#else
return NULL;
#endif
DPRINTF("sio_open: %s: unknown device type\n", str);
return NULL;
@ -169,7 +165,7 @@ sio_setpar(struct sio_hdl *hdl, struct sio_par *par)
return 0;
}
if (par->__magic != SIO_PAR_MAGIC) {
DPRINTF("sio_setpar: use of uninitialized sio_par structure\n");
DPRINTF("sio_setpar: uninitialized sio_par structure\n");
hdl->eof = 1;
return 0;
}
@ -452,7 +448,7 @@ sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr)
#ifdef DEBUG
void
_sio_printpos(struct sio_hdl *hdl)
{
{
struct timespec ts;
long long rpos, rdiff;
long long cpos, cdiff;
@ -465,7 +461,7 @@ _sio_printpos(struct sio_hdl *hdl)
rround = hdl->par.round * rbpf;
wround = hdl->par.round * wbpf;
rpos = (hdl->mode & SIO_REC) ?
rpos = (hdl->mode & SIO_REC) ?
hdl->cpos * rbpf - hdl->rused : 0;
wpos = (hdl->mode & SIO_PLAY) ?
hdl->cpos * wbpf + hdl->wused : 0;

View File

@ -39,7 +39,7 @@
#ifdef DEBUG
static snd_output_t *output = NULL;
#define DALSA(str, err) fprintf(stderr, "%s: %s\n", str, snd_strerror(err))
#define DALSA(str, err) fprintf(stderr, "%s: %s\n", str, snd_strerror(err))
#else
#define DALSA(str, err) do {} while (0)
#endif
@ -103,9 +103,9 @@ static unsigned int cap_rates[] = {
};
static snd_pcm_format_t cap_fmts[] = {
/* XXX add s24le3 and s24be3 */
SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_BE,
SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_BE,
SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_U8
};
@ -275,17 +275,23 @@ sio_alsa_enctofmt(struct sio_alsa_hdl *hdl, snd_pcm_format_t *rfmt,
struct sio_hdl *
_sio_alsa_open(const char *str, unsigned mode, int nbio)
{
const char *p;
struct sio_alsa_hdl *hdl;
struct sio_par par;
size_t len;
int err;
switch (*str) {
p = _sndio_parsetype(str, "rsnd");
if (p == NULL) {
DPRINTF("_sio_alsa_open: %s: \"rsnd\" expected\n", str);
return NULL;
}
switch (*p) {
case '/':
str++;
p++;
break;
default:
DPRINTF("_sio_alsa_open: %s: '/<devnum>' expected\n", str);
DPRINTF("_sio_alsa_open: %s: '/' expected\n", str);
return NULL;
}
hdl = malloc(sizeof(struct sio_alsa_hdl));
@ -298,12 +304,12 @@ _sio_alsa_open(const char *str, unsigned mode, int nbio)
if (err < 0)
DALSA("couldn't attach to stderr", err);
#endif
len = strlen(str);
len = strlen(p);
hdl->devname = malloc(len + sizeof(DEVNAME_PREFIX));
if (hdl->devname == NULL)
goto bad_free_hdl;
memcpy(hdl->devname, DEVNAME_PREFIX, sizeof(DEVNAME_PREFIX) - 1);
memcpy(hdl->devname + sizeof(DEVNAME_PREFIX) - 1, str, len + 1);
memcpy(hdl->devname + sizeof(DEVNAME_PREFIX) - 1, p, len + 1);
if (mode & SIO_PLAY) {
err = snd_pcm_open(&hdl->opcm, hdl->devname,
SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
@ -492,11 +498,11 @@ sio_alsa_xrun(struct sio_alsa_hdl *hdl)
*
* to understand the formula, draw a picture :)
*/
rbpf = (hdl->sio.mode & SIO_REC) ?
rbpf = (hdl->sio.mode & SIO_REC) ?
hdl->sio.par.bps * hdl->sio.par.rchan : 1;
wbpf = (hdl->sio.mode & SIO_PLAY) ?
hdl->sio.par.bps * hdl->sio.par.pchan : 1;
rround = hdl->sio.par.round * rbpf;
rround = hdl->sio.par.round * rbpf;
clk = hdl->sio.cpos % hdl->sio.par.round;
rdrop = (clk * rbpf - hdl->sio.rused) % rround;
@ -531,12 +537,12 @@ sio_alsa_setpar_hw(snd_pcm_t *pcm, snd_pcm_hw_params_t *hwp,
snd_pcm_uframes_t *round, unsigned int *periods)
{
static snd_pcm_format_t fmts[] = {
SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_U32_LE, SND_PCM_FORMAT_U32_BE,
SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_BE,
SND_PCM_FORMAT_S24_LE, SND_PCM_FORMAT_S24_BE,
SND_PCM_FORMAT_U24_LE, SND_PCM_FORMAT_U24_BE,
SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_U16_LE, SND_PCM_FORMAT_U16_BE,
SND_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_U16_LE, SND_PCM_FORMAT_U16_BE,
SND_PCM_FORMAT_U8, SND_PCM_FORMAT_S8
};
int i, err, dir = 0;
@ -693,7 +699,7 @@ sio_alsa_getcap(struct sio_hdl *sh, struct sio_cap *cap)
sio_alsa_fmttopar(hdl, cap_fmts[i],
&cap->enc[i].bits,
&cap->enc[i].sig,
&cap->enc[i].le);
&cap->enc[i].le);
cap->enc[i].bps = SIO_BPS(cap->enc[0].bits);
cap->enc[i].msb = 1;
}
@ -790,7 +796,7 @@ sio_alsa_setpar(struct sio_hdl *sh, struct sio_par *par)
ofmt, orate, (unsigned int)oround, operiods);
DPRINTFN(2, "ifmt = %u, irate = %u, iround = %u, iperiods = %u\n",
ifmt, irate, (unsigned int)iround, iperiods);
if (ifmt != ofmt) {
DPRINTF("play and rec formats differ\n");
hdl->sio.eof = 1;
@ -1116,7 +1122,7 @@ sio_alsa_revents(struct sio_hdl *sh, struct pollfd *pfd)
if (hdl->sio.eof)
return POLLHUP;
for (i = 0; i < hdl->onfds + hdl->infds; i++) {
DPRINTFN(4, "sio_alsa_revents: pfds[%d].revents = %x\n",
i, pfd[i].revents);

View File

@ -125,7 +125,8 @@ sio_aucat_runmsg(struct sio_aucat_hdl *hdl)
hdl->pstate = PSTATE_INIT;
break;
default:
DPRINTF("sio_aucat_runmsg: unhandled message %u\n", hdl->aucat.rmsg.cmd);
DPRINTF("sio_aucat_runmsg: unhandled message %u\n",
hdl->aucat.rmsg.cmd);
hdl->sio.eof = 1;
return 0;
}
@ -156,7 +157,7 @@ _sio_aucat_open(const char *str, unsigned int mode, int nbio)
hdl = malloc(sizeof(struct sio_aucat_hdl));
if (hdl == NULL)
return NULL;
if (!_aucat_open(&hdl->aucat, str, mode, 0)) {
if (!_aucat_open(&hdl->aucat, str, mode)) {
free(hdl);
return NULL;
}

View File

@ -80,7 +80,7 @@ The
library allows user processes to access
.Xr audio 4
hardware and the
.Xr sndiod 1
.Xr sndiod 8
audio server in a uniform way.
.Ss Opening and closing an audio device
First the application must call the
@ -255,7 +255,7 @@ has been called,
must be called before parameters can be changed.
.Pp
If the device is exposed by the
.Xr sndiod 1
.Xr sndiod 8
server, which is the default configuration,
a transparent emulation layer will
automatically be set up, and in this case any combination of
@ -709,9 +709,9 @@ The debug level:
may be a value between 0 and 2.
.El
.Sh SEE ALSO
.Xr sndiod 1 ,
.Xr audio 4 ,
.Xr sndio 7 ,
.Xr sndiod 8 ,
.Xr audio 9
.Sh BUGS
The
@ -727,7 +727,7 @@ function will stop playback immediately.
If the application doesn't consume recorded data fast enough then
.Dq "control messages"
from the
.Xr sndiod 1
.Xr sndiod 8
server are delayed and consequently
.Fn sio_onmove
callback or volume changes may be delayed.

View File

@ -330,46 +330,57 @@ sio_sun_getcap(struct sio_hdl *sh, struct sio_cap *cap)
#undef NRATES
}
struct sio_hdl *
_sio_sun_open(const char *str, unsigned int mode, int nbio)
static int
sio_sun_getfd(const char *str, unsigned int mode, int nbio)
{
int fd, flags, fullduplex;
struct audio_info aui;
struct sio_sun_hdl *hdl;
struct sio_par par;
const char *p;
char path[DEVPATH_MAX];
unsigned int devnum;
int fd, flags;
switch (*str) {
p = _sndio_parsetype(str, "rsnd");
if (p == NULL) {
DPRINTF("sio_sun_getfd: %s: \"rsnd\" expected\n", str);
return -1;
}
switch (*p) {
case '/':
str++;
p++;
break;
default:
DPRINTF("_sio_sun_open: %s: '/<devnum>' expected\n", str);
return NULL;
DPRINTF("sio_sun_getfd: %s: '/' expected\n", str);
return -1;
}
str = _sndio_parsenum(str, &devnum, 255);
if (str == NULL || *str != '\0') {
DPRINTF("_sio_sun_open: can't determine device number\n");
return NULL;
p = _sndio_parsenum(p, &devnum, 255);
if (p == NULL || *p != '\0') {
DPRINTF("sio_sun_getfd: %s: number expected after '/'\n", str);
return -1;
}
hdl = malloc(sizeof(struct sio_sun_hdl));
if (hdl == NULL)
return NULL;
_sio_create(&hdl->sio, &sio_sun_ops, mode, nbio);
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);
goto bad_free;
return -1;
}
return fd;
}
static struct sio_hdl *
sio_sun_fdopen(int fd, unsigned int mode, int nbio)
{
struct audio_info aui;
struct sio_sun_hdl *hdl;
struct sio_par par;
hdl = malloc(sizeof(struct sio_sun_hdl));
if (hdl == NULL)
return NULL;
_sio_create(&hdl->sio, &sio_sun_ops, mode, nbio);
/*
* pause the device
@ -381,18 +392,7 @@ _sio_sun_open(const char *str, unsigned int mode, int nbio)
aui.record.pause = 1;
if (ioctl(fd, AUDIO_SETINFO, &aui) < 0) {
DPERROR("sio_open_sun: setinfo");
goto bad_close;
}
/*
* If both play and record are requested then
* set full duplex mode.
*/
if (mode == (SIO_PLAY | SIO_REC)) {
fullduplex = 1;
if (ioctl(fd, AUDIO_SETFD, &fullduplex) < 0) {
DPRINTF("sio_open_sun: %s: can't set full-duplex\n", path);
goto bad_close;
}
goto bad_free;
}
hdl->fd = fd;
@ -410,16 +410,30 @@ _sio_sun_open(const char *str, unsigned int mode, int nbio)
par.bits = 16;
par.appbufsz = 1200;
if (!sio_setpar(&hdl->sio, &par))
goto bad_close;
goto bad_free;
return (struct sio_hdl *)hdl;
bad_close:
while (close(fd) < 0 && errno == EINTR)
; /* retry */
bad_free:
free(hdl);
return NULL;
}
struct sio_hdl *
_sio_sun_open(const char *str, unsigned int mode, int nbio)
{
struct sio_hdl *hdl;
int fd;
fd = sio_sun_getfd(str, mode, nbio);
if (fd < 0)
return NULL;
hdl = sio_sun_fdopen(fd, mode, nbio);
if (hdl != NULL)
return hdl;
while (close(fd) < 0 && errno == EINTR)
; /* retry */
return NULL;
}
static void
sio_sun_close(struct sio_hdl *sh)
{
@ -546,7 +560,8 @@ sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
}
DPRINTFN(2, "sio_sun_setpar: %i: trying pars = %u/%u/%u\n",
i, rate, prec, enc);
if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0 && errno != EINVAL) {
if (ioctl(hdl->fd, AUDIO_SETINFO, &aui) < 0 &&
errno != EINVAL) {
DPERROR("sio_sun_setpar: setinfo(pars)");
hdl->sio.eof = 1;
return 0;
@ -567,7 +582,7 @@ sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
case AUDIO_ENCODING_ULINEAR:
break;
default:
DPRINTF("sio_sun_setpar: couldn't set linear encoding\n");
DPRINTF("sio_sun_setpar: couldn't find encoding\n");
hdl->sio.eof = 1;
return 0;
}
@ -658,7 +673,7 @@ sio_sun_setpar(struct sio_hdl *sh, struct sio_par *par)
}
infr = aui.record.block_size / ibpf;
onfr = aui.play.block_size / obpf;
DPRINTFN(2, "sio_sun_setpar: %i: trying round = %u -> (%u, %u)\n",
DPRINTFN(2, "sio_sun_setpar: %i: round = %u -> (%u, %u)\n",
i, round, infr, onfr);
/*
@ -788,7 +803,7 @@ sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
DPERROR("sio_sun_write: write");
hdl->sio.eof = 1;
}
return 0;
return 0;
}
if (hdl->filling) {
if (!sio_sun_autostart(hdl))
@ -833,7 +848,7 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
doerr = (ap.play_xrun - hdl->oerr) / hdl->obpf;
hdl->obytes = ap.play_pos;
hdl->oerr = ap.play_xrun;
hdl->odelta += delta;
hdl->odelta += delta;
if (!(hdl->sio.mode & SIO_REC)) {
hdl->idelta += delta;
dierr = doerr;

View File

@ -36,7 +36,6 @@ siomix_open(const char *str, unsigned int mode, int nbio)
{
static char devany[] = SIOMIX_DEVANY;
struct siomix_hdl *hdl;
const char *p;
#ifdef DEBUG
_sndio_debug_init();
@ -49,27 +48,26 @@ siomix_open(const char *str, unsigned int mode, int nbio)
str = devany;
}
if (strcmp(str, devany) == 0) {
hdl = _siomix_aucat_open("/0", mode, nbio);
hdl = _siomix_aucat_open("snd/0", mode, nbio);
if (hdl != NULL)
return hdl;
#if defined(USE_SUN_MIXER)
return _siomix_sun_open("/0", mode, nbio);
return _siomix_sun_open("rsnd/0", mode, nbio);
#elif defined(USE_ALSA_MIXER)
return _siomix_alsa_open("/0", mode, nbio);
return _siomix_alsa_open("rsnd/0", mode, nbio);
#else
return NULL;
#endif
}
if ((p = _sndio_parsetype(str, "snd")) != NULL)
return _siomix_aucat_open(p, mode, nbio);
#if defined(USE_ALSA_MIXER) || defined(USE_SUN_MIXER)
if ((p = _sndio_parsetype(str, "rsnd")) != NULL) {
if (_sndio_parsetype(str, "snd"))
return _siomix_aucat_open(str, mode, nbio);
if (_sndio_parsetype(str, "rsnd"))
#if defined(USE_SUN_MIXER)
return _siomix_sun_open(p, mode, nbio);
return _siomix_sun_open(str, mode, nbio);
#elif defined(USE_ALSA_MIXER)
return _siomix_alsa_open(p, mode, nbio);
#endif
}
return _siomix_alsa_open(str, mode, nbio);
#else
return NULL;
#endif
DPRINTF("siomix_open: %s: unknown device type\n", str);
return NULL;

View File

@ -143,7 +143,7 @@ _siomix_aucat_open(const char *str, unsigned int mode, int nbio)
hdl = malloc(sizeof(struct siomix_aucat_hdl));
if (hdl == NULL)
return NULL;
if (!_aucat_open(&hdl->aucat, str, mode, 0)) {
if (!_aucat_open(&hdl->aucat, str, mode)) {
free(hdl);
return NULL;
}

View File

@ -32,8 +32,15 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "debug.h"
#include "siomix_priv.h"
#include "bsd-compat.h"
#define DEVPATH_PREFIX "/dev/mixer"
#define DEVPATH_MAX (1 + \
sizeof(DEVPATH_PREFIX) - 1 + \
sizeof(int) * 3)
#define SUN_TO_SIOMIX(v) (((v) * 127 + 127) / 255)
#define SIOMIX_TO_SUN(v) (((v) * 255 + 63) / 127)
@ -261,38 +268,77 @@ scanvol(struct siomix_sun_hdl *hdl, struct wskbd_vol *vol)
return 1;
}
struct siomix_hdl *
_siomix_sun_open(const char *str, unsigned int mode, int nbio)
static int
siomix_sun_getfd(const char *str, unsigned int mode, int nbio)
{
struct siomix_sun_hdl *hdl;
char path[PATH_MAX];
int flags;
const char *p;
char path[DEVPATH_MAX];
unsigned int devnum;
int fd, flags;
if (*str != '/') {
DPRINTF("siomix_sun_open: %s: '/<devnum>' expected\n", str);
return NULL;
p = _sndio_parsetype(str, "rsnd");
if (p == NULL) {
DPRINTF("siomix_sun_getfd: %s: \"rsnd\" expected\n", str);
return -1;
}
str++;
hdl = malloc(sizeof(struct siomix_sun_hdl));
if (hdl == NULL)
return NULL;
_siomix_create(&hdl->siomix, &siomix_sun_ops, mode, nbio);
snprintf(path, sizeof(path), "/dev/mixer%s", str);
switch (*p) {
case '/':
p++;
break;
default:
DPRINTF("siomix_sun_getfd: %s: '/' expected\n", str);
return -1;
}
p = _sndio_parsenum(p, &devnum, 255);
if (p == NULL || *p != '\0') {
DPRINTF("siomix_sun_getfd: %s: number expected after '/'\n", str);
return -1;
}
snprintf(path, sizeof(path), DEVPATH_PREFIX "%u", devnum);
if (mode == (SIOMIX_READ | SIOMIX_WRITE))
flags = O_RDWR;
else
flags = (mode & SIOMIX_WRITE) ? O_WRONLY : O_RDONLY;
while ((hdl->fd = open(path, flags | O_CLOEXEC)) < 0) {
while ((fd = open(path, flags | O_NONBLOCK | O_CLOEXEC)) < 0) {
if (errno == EINTR)
continue;
DPERROR(path);
free(hdl);
return NULL;
return -1;
}
return fd;
}
static struct siomix_hdl *
siomix_sun_fdopen(int fd, unsigned int mode, int nbio)
{
struct siomix_sun_hdl *hdl;
hdl = malloc(sizeof(struct siomix_sun_hdl));
if (hdl == NULL)
return NULL;
_siomix_create(&hdl->siomix, &siomix_sun_ops, mode, nbio);
hdl->fd = fd;
init(hdl);
return (struct siomix_hdl *)hdl;
}
struct siomix_hdl *
_siomix_sun_open(const char *str, unsigned int mode, int nbio)
{
struct siomix_hdl *hdl;
int fd;
fd = siomix_sun_getfd(str, mode, nbio);
if (fd < 0)
return NULL;
hdl = siomix_sun_fdopen(fd, mode, nbio);
if (hdl != NULL)
return hdl;
while (close(fd) < 0 && errno == EINTR)
; /* retry */
return NULL;
}
static void
siomix_sun_close(struct siomix_hdl *addr)
{

View File

@ -25,7 +25,7 @@ The
.Nm sndio
audio and MIDI system provides access to audio and MIDI hardware and
to services provided by
.Xr sndiod 1 ,
.Xr sndiod 8 ,
summarized below.
.Pp
Hardware
@ -38,7 +38,7 @@ audio programs.
.Pp
To overcome hardware limitations and to allow multiple applications
to share the hardware,
.Xr sndiod 1
.Xr sndiod 8
can be used.
It exposes one or more software sub-devices backed by the underlying hardware,
while doing all necessary conversions on the fly.
@ -57,15 +57,15 @@ Software MIDI thru boxes allow one application to send MIDI data to other
applications connected to the thru box (for instance a software sequencer
can send events to multiple software synthesizers).
There's no hardware involved: thru boxes are created by
.Xr sndiod 1 .
.Xr sndiod 8 .
.Pp
Additionally,
.Xr sndiod 1
.Xr sndiod 8
exposes a MIDI port used to control and monitor audio streams
in real time using MIDI.
.Sh DEVICE NAMES
From the user's perspective every audio interface, MIDI port, and
.Xr sndiod 1
.Xr sndiod 8
service has a name of the form:
.Bd -literal -offset center
type[@hostname][,unit]/devnum[.option]
@ -89,36 +89,36 @@ Raw
port.
.It Pa snd
Audio device exposed by
.Xr sndiod 1 .
.Xr sndiod 8 .
.It Pa midithru
MIDI thru box created with
.Xr sndiod 1 .
.Xr sndiod 8 .
.It Pa midi
MIDI port exposed by
.Xr sndiod 1 .
.Xr sndiod 8 .
.It Pa default
Default audio device or MIDI port (see below).
.El
.It Pa hostname
The hostname or address where the remote
.Xr sndiod 1
.Xr sndiod 8
server to connect to is running.
.It Pa unit
The number of the
.Xr sndiod 1
.Xr sndiod 8
server to connect to, corresponding to the integer specified using the
.Fl U
option of
.Xr sndiod 1 .
.Xr sndiod 8 .
Useful only if multiple
.Xr sndiod 1
.Xr sndiod 8
servers are running on the same system.
.It Pa devnum
Device number.
For hardware audio or MIDI ports, this corresponds to
the character device minor number.
For audio devices or MIDI ports created with
.Xr sndiod 1
.Xr sndiod 8
it corresponds to the number of the corresponding
.Fl fq
option on the command line.
@ -126,7 +126,7 @@ option on the command line.
Corresponds to the sub-device string registered using the
.Fl s
option of
.Xr sndiod 1 .
.Xr sndiod 8 .
.El
.Pp
For example:
@ -138,13 +138,13 @@ First hardware audio device.
Hardware MIDI port number 5.
.It Pa snd/0
First audio device exposed by
.Xr sndiod 1 .
.Xr sndiod 8 .
.It Pa snd/0.rear
Sub-device registered with
.Fl s Fa rear .
.It Pa midithru/0
First MIDI thru box created with
.Xr sndiod 1 .
.Xr sndiod 8 .
.El
.Sh DEFAULTS
If
@ -158,10 +158,10 @@ If it is not set, the program first tries to connect to
If that fails, it then tries to use
.Pa rsnd/0 .
This allows the
.Xr sndiod 1
.Xr sndiod 8
audio server to be used by default and the bare hardware as fallback;
programs don't have to be reconfigured when
.Xr sndiod 1
.Xr sndiod 8
is started or stopped.
.Pp
If
@ -175,14 +175,14 @@ If it is not set, the program first tries to connect to
If that fails, it then tries to use
.Pa rmidi/0 .
As long as
.Xr sndiod 1
.Xr sndiod 8
is running, this allows programs to exchange MIDI data on
machines with no MIDI hardware by default, e.g. a MIDI player
could use a software synthesizer with no manual configuration
required.
.Sh AUTHENTICATION
If a shared
.Xr sndiod 1
.Xr sndiod 8
server is running, for privacy reasons only one user may have
connections to it at a given time
(though the same user could have multiple connections to it).
@ -216,8 +216,8 @@ Audio devices.
MIDI ports.
.El
.Sh SEE ALSO
.Xr sndiod 1 ,
.Xr mio_open 3 ,
.Xr sio_open 3 ,
.Xr audio 4 ,
.Xr midi 4
.Xr midi 4 ,
.Xr sndiod 8

View File

@ -14,29 +14,29 @@ LDADD = -lsndio @ldadd@
@vars@
#
# binaries, documentation, man pages and examples will be installed in
# ${BIN_DIR}, ${MAN1_DIR}
# binaries, documentation, man pages and examples will be installed in
# ${BIN_DIR}, ${MAN8_DIR}
#
BIN_DIR = @bindir@
MAN1_DIR = @mandir@/man1
MAN8_DIR = @mandir@/man8
#
# programs to build
#
PROG = sndiod
MAN1 = sndiod.1
MAN8 = sndiod.8
all: ${PROG} ${MAN1}
all: ${PROG}
install:
mkdir -p ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN1_DIR}
rm -f ${DESTDIR}${BIN_DIR}/${PROG} ${DESTDIR}${MAN1_DIR}/${MAN1}
mkdir -p ${DESTDIR}${BIN_DIR} ${DESTDIR}${MAN8_DIR}
rm -f ${DESTDIR}${BIN_DIR}/${PROG} ${DESTDIR}${MAN8_DIR}/${MAN8}
cp ${PROG} ${DESTDIR}${BIN_DIR}
cp ${MAN1} ${DESTDIR}${MAN1_DIR}
cp ${MAN8} ${DESTDIR}${MAN8_DIR}
uninstall:
cd ${DESTDIR}${BIN_DIR} && rm -f ${PROG}
cd ${DESTDIR}${MAN1_DIR} && rm -f ${MAN1}
cd ${DESTDIR}${MAN8_DIR} && rm -f ${MAN8}
clean:
rm -f -- *.o ${PROG}

View File

@ -53,7 +53,7 @@ abuf_init(struct abuf *buf, unsigned int len)
void
abuf_done(struct abuf *buf)
{
#ifdef DEBUG
#ifdef DEBUG
if (buf->used > 0) {
if (log_level >= 3) {
log_puts("deleting non-empty buffer, used = ");

View File

@ -557,12 +557,12 @@ slot_skip(struct slot *s)
if (s->mode & MODE_PLAY) {
abuf_rdiscard(&s->mix.buf, s->round * s->mix.bpf);
}
s->skip--;
s->skip--;
}
return max - s->skip;
}
int
int
play_filt_resamp(struct slot *s, void *res_in, void *out, int todo)
{
int i, offs, vol, nch;
@ -592,7 +592,7 @@ play_filt_resamp(struct slot *s, void *res_in, void *out, int todo)
return todo;
}
int
int
play_filt_dec(struct slot *s, void *in, void *out, int todo)
{
void *tmp;
@ -673,7 +673,7 @@ dev_mix_adjvol(struct dev *d)
}
}
int
int
rec_filt_resamp(struct slot *s, void *in, void *res_out, int todo)
{
int i, vol, offs, nch;
@ -702,7 +702,7 @@ rec_filt_resamp(struct slot *s, void *in, void *res_out, int todo)
return todo;
}
int
int
rec_filt_enc(struct slot *s, void *in, void *out, int todo)
{
void *tmp;
@ -857,11 +857,11 @@ dev_cycle(struct dev *d)
dev_mix_adjvol(d);
continue;
}
/*
* check for xruns
*/
if (((s->mode & MODE_PLAY) &&
if (((s->mode & MODE_PLAY) &&
s->mix.buf.used < s->round * s->mix.bpf) ||
((s->mode & MODE_RECMASK) &&
s->sub.buf.len - s->sub.buf.used <
@ -927,7 +927,7 @@ void
dev_onmove(struct dev *d, int delta)
{
long long pos;
struct slot *s, *snext;
struct slot *s, *snext;
d->delta += delta;
@ -1256,6 +1256,8 @@ dev_done(struct dev *d)
log_puts(": draining\n");
}
#endif
if (d->tstate != MMC_STOP)
dev_mmcstop(d);
if (d->hold)
dev_unref(d);
}
@ -1266,7 +1268,7 @@ dev_bynum(int num)
struct dev *d;
for (d = dev_list; d != NULL; d = d->next) {
if (num-- == 0)
if (d->num == num)
return d;
}
return NULL;
@ -1327,11 +1329,11 @@ dev_wakeup(struct dev *d)
}
d->poffs = 0;
/*
/*
* empty cycles don't increment delta, so it's ok to
* start at 0
**/
d->delta = 0;
d->delta = 0;
d->pstate = DEV_RUN;
dev_sio_start(d);
@ -1650,7 +1652,7 @@ slot_attach(struct slot *s)
* start the device if not started
*/
dev_wakeup(d);
/*
* get the current position, the origin is when the first sample
* played and/or recorded
@ -1684,7 +1686,7 @@ slot_attach(struct slot *s)
#ifdef DEBUG
if ((s->mode & d->mode) != s->mode) {
slot_log(s);
log_puts(": mode beyond device mode, not attaching\n");
log_puts(": mode beyond device mode, not attaching\n");
panic();
}
#endif
@ -1752,7 +1754,7 @@ slot_attach(struct slot *s)
s->sub.encbuf =
xmalloc(s->round * slot_nch * sizeof(adata_t));
}
/*
* N-th recorded block is the N-th played block
*/
@ -1770,7 +1772,7 @@ slot_ready(struct slot *s)
/*
* device may be disconnected, and if so we're called from
* slot->ops->exit() on a closed device
*/
*/
if (s->dev->pstate == DEV_CFG)
return;
if (s->tstate == MMC_OFF)
@ -1811,7 +1813,7 @@ slot_start(struct slot *s)
log_puts("\n");
}
#endif
s->mix.bpf = s->par.bps *
s->mix.bpf = s->par.bps *
(s->mix.slot_cmax - s->mix.slot_cmin + 1);
abuf_init(&s->mix.buf, bufsz * s->mix.bpf);
}
@ -1826,7 +1828,7 @@ slot_start(struct slot *s)
log_puts("\n");
}
#endif
s->sub.bpf = s->par.bps *
s->sub.bpf = s->par.bps *
(s->sub.slot_cmax - s->sub.slot_cmin + 1);
abuf_init(&s->sub.buf, bufsz * s->sub.bpf);
}
@ -1871,7 +1873,7 @@ slot_detach(struct slot *s)
panic();
}
#endif
}
}
*ps = s->next;
if (s->mode & MODE_RECMASK) {
if (s->sub.encbuf)

View File

@ -46,7 +46,7 @@ struct slot {
void *arg; /* user data for callbacks */
struct aparams par; /* socket side params */
struct {
int weight; /* dynamic range */
int weight; /* dynamic range */
int maxweight; /* max dynamic range allowed */
unsigned int vol; /* volume within the vol */
struct abuf buf; /* socket side buffer */
@ -140,7 +140,7 @@ struct dev {
/*
* audio device (while opened)
*/
*/
struct dev_sio sio;
struct dev_siomix siomix;
struct aparams par; /* encoding */

View File

@ -152,7 +152,7 @@ aparams_strtoenc(struct aparams *par, char *istr)
return 0;
done:
par->msb = msb;
par->msb = msb;
par->sig = sig;
par->bits = bits;
par->bps = bps;
@ -296,7 +296,8 @@ resamp_do(struct resamp *p, adata_t *in, adata_t *out, int todo)
* initialize resampler with ibufsz/obufsz factor and "nch" channels
*/
void
resamp_init(struct resamp *p, unsigned int iblksz, unsigned int oblksz, int nch)
resamp_init(struct resamp *p, unsigned int iblksz,
unsigned int oblksz, int nch)
{
unsigned int i;
@ -446,7 +447,7 @@ enc_init(struct conv *p, struct aparams *par, int nch)
p->bias = (1U << 31) >> p->shift;
} else {
p->bias = 0;
}
}
if (!par->le) {
p->bfirst = par->bps - 1;
p->bnext = -1;
@ -539,7 +540,7 @@ dec_init(struct conv *p, struct aparams *par, int nch)
p->bias = (1U << 31) >> p->shift;
} else {
p->bias = 0;
}
}
if (par->le) {
p->bfirst = par->bps - 1;
p->bnext = -1;

View File

@ -105,9 +105,9 @@ typedef int adata_t;
struct aparams {
unsigned int bps; /* bytes per sample */
unsigned int bits; /* actually used bits */
unsigned int le; /* 1 if little endian, 0 if big endian */
unsigned int le; /* 1 if little endian, else be */
unsigned int sig; /* 1 if signed, 0 if unsigned */
unsigned int msb; /* 1 if msb justified, 0 if lsb justified */
unsigned int msb; /* 1 if msb justified, else lsb */
};
struct resamp {
@ -124,7 +124,7 @@ struct conv {
int bfirst; /* bytes to skip at startup */
unsigned int bps; /* bytes per sample */
unsigned int shift; /* shift to get 32bit MSB */
unsigned int bias; /* bias of unsigned samples */
unsigned int bias; /* bias of unsigned samples */
int bnext; /* to reach the next byte */
int snext; /* to reach the next sample */
int nch;

View File

@ -46,7 +46,6 @@
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
@ -259,7 +258,7 @@ file_del(struct file *f)
log_puts("bad state in file_del()\n");
panic();
}
#endif
#endif
file_nfds -= f->max_nfds;
f->state = FILE_ZOMB;
#ifdef DEBUG
@ -283,7 +282,7 @@ file_process(struct file *f, struct pollfd *pfd)
if (log_level >= 3)
clock_gettime(CLOCK_MONOTONIC, &ts0);
#endif
revents = (f->state != FILE_ZOMB) ?
revents = (f->state != FILE_ZOMB) ?
f->ops->revents(f->arg, pfd) : 0;
if ((revents & POLLHUP) && (f->state != FILE_ZOMB))
f->ops->hup(f->arg);
@ -399,8 +398,10 @@ file_poll(void)
timo = -1;
res = poll(pfds, nfds, timo);
if (res < 0) {
if (errno != EINTR)
err(1, "poll");
if (errno != EINTR) {
log_puts("poll failed");
panic();
}
return 1;
}
@ -412,19 +413,17 @@ file_poll(void)
file_wtime += 1000000000LL * (ts.tv_sec - sleepts.tv_sec);
file_wtime += ts.tv_nsec - sleepts.tv_nsec;
#endif
delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
#ifdef DEBUG
if (delta_nsec < 0)
log_puts("file_poll: negative time interval\n");
#endif
file_ts = ts;
if (delta_nsec >= 0 && delta_nsec < 1000000000LL)
timo_update(delta_nsec / 1000);
else {
if (log_level >= 2)
log_puts("ignored huge clock delta\n");
if (timo_queue) {
delta_nsec = 1000000000LL * (ts.tv_sec - file_ts.tv_sec);
delta_nsec += ts.tv_nsec - file_ts.tv_nsec;
if (delta_nsec >= 0 && delta_nsec < 60000000000LL)
timo_update(delta_nsec / 1000);
else {
if (log_level >= 2)
log_puts("out-of-bounds clock delta\n");
}
}
file_ts = ts;
/*
* process files that rely on poll
@ -444,15 +443,14 @@ filelist_init(void)
{
sigset_t set;
sigemptyset(&set);
(void)sigaddset(&set, SIGPIPE);
if (sigprocmask(SIG_BLOCK, &set, NULL))
err(1, "sigprocmask");
file_list = NULL;
if (clock_gettime(CLOCK_MONOTONIC, &file_ts) < 0) {
perror("clock_gettime");
exit(1);
log_puts("filelist_init: CLOCK_MONOTONIC unsupported\n");
panic();
}
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
sigprocmask(SIG_BLOCK, &set, NULL);
file_list = NULL;
log_sync = 0;
timo_init();
}

View File

@ -34,7 +34,7 @@ struct fileops {
char *name;
int (*pollfd)(void *, struct pollfd *);
int (*revents)(void *, struct pollfd *);
/*
/*
* we have to handle POLLIN and POLLOUT events
* in separate handles, since handling POLLIN can
* close the file, and continuing (to handle POLLOUT)
@ -52,7 +52,7 @@ struct file {
#define FILE_INIT 0 /* ready */
#define FILE_ZOMB 1 /* closed, but not free()d yet */
unsigned int state; /* one of above */
unsigned int max_nfds; /* max number of descriptors */
unsigned int max_nfds; /* max number of descriptors */
unsigned int nfds; /* number of descriptors polled */
char *name; /* for debug purposes */
};

View File

@ -24,12 +24,10 @@
#include <netinet/tcp.h>
#include <netdb.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -80,7 +78,7 @@ listen_close(struct listen *f)
xfree(f);
}
void
int
listen_new_un(char *path)
{
int sock, oldumask;
@ -89,11 +87,13 @@ listen_new_un(char *path)
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(1);
log_puts(path);
log_puts(": failed to create socket\n");
return 0;
}
if (unlink(path) < 0 && errno != ENOENT) {
perror("unlink");
log_puts(path);
log_puts(": failed to unlink socket\n");
goto bad_close;
}
sockname.sun_family = AF_UNIX;
@ -101,11 +101,13 @@ listen_new_un(char *path)
oldumask = umask(0111);
if (bind(sock, (struct sockaddr *)&sockname,
sizeof(struct sockaddr_un)) < 0) {
perror("bind");
log_puts(path);
log_puts(": failed to bind socket\n");
goto bad_close;
}
if (listen(sock, 1) < 0) {
perror("listen");
log_puts(path);
log_puts(": failed to listen\n");
goto bad_close;
}
umask(oldumask);
@ -114,29 +116,25 @@ listen_new_un(char *path)
if (f->file == NULL)
goto bad_close;
f->path = xstrdup(path);
if (f->path == NULL) {
perror("strdup");
exit(1);
}
f->fd = sock;
f->next = listen_list;
listen_list = f;
return;
return 1;
bad_close:
close(sock);
exit(1);
return 0;
}
void
int
listen_new_tcp(char *addr, unsigned int port)
{
char *host, serv[sizeof(unsigned int) * 3 + 1];
struct addrinfo *ailist, *ai, aihints;
struct listen *f;
int s, error, opt = 1, n = 0;
/*
* obtain a list of possible addresses for the host/port
/*
* obtain a list of possible addresses for the host/port
*/
memset(&aihints, 0, sizeof(struct addrinfo));
snprintf(serv, sizeof(serv), "%u", port);
@ -146,24 +144,27 @@ listen_new_tcp(char *addr, unsigned int port)
aihints.ai_protocol = IPPROTO_TCP;
error = getaddrinfo(host, serv, &aihints, &ailist);
if (error) {
fprintf(stderr, "%s: %s\n", addr, gai_strerror(error));
exit(1);
log_puts(addr);
log_puts(": failed to resolve address\n");
return 0;
}
/*
/*
* for each address, try create a listening socket bound on
* that address
*/
for (ai = ailist; ai != NULL; ai = ai->ai_next) {
s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (s < 0) {
perror("socket");
log_puts(addr);
log_puts(": failed to create socket\n");
continue;
}
opt = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
&opt, sizeof(int)) < 0) {
perror("setsockopt");
log_puts(addr);
log_puts(": failed to set SO_REUSEADDR\n");
goto bad_close;
}
if (ai->ai_family == AF_INET6) {
@ -175,17 +176,20 @@ listen_new_tcp(char *addr, unsigned int port)
opt = 1;
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
&opt, sizeof(int)) < 0) {
perror("setsockopt");
log_puts(addr);
log_puts(": failed to set IPV6_V6ONLY\n");
goto bad_close;
}
}
if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) {
perror("bind");
log_puts(addr);
log_puts(": failed to bind socket\n");
goto bad_close;
}
if (listen(s, 1) < 0) {
perror("listen");
log_puts(addr);
log_puts(": failed to listen\n");
goto bad_close;
}
f = xmalloc(sizeof(struct listen));
@ -202,8 +206,7 @@ listen_new_tcp(char *addr, unsigned int port)
n++;
}
freeaddrinfo(ailist);
if (n == 0)
exit(1);
return n;
}
int
@ -217,7 +220,8 @@ listen_pollfd(void *arg, struct pollfd *pfd)
{
struct listen *f = arg;
if (file_slowaccept)
f->slowaccept = file_slowaccept;
if (f->slowaccept)
return 0;
pfd->fd = f->fd;
pfd->events = POLLIN;
@ -227,6 +231,10 @@ listen_pollfd(void *arg, struct pollfd *pfd)
int
listen_revents(void *arg, struct pollfd *pfd)
{
struct listen *f = arg;
if (f->slowaccept)
return 0;
return pfd->revents;
}
@ -244,12 +252,11 @@ listen_in(void *arg)
continue;
if (errno == ENFILE || errno == EMFILE)
file_slowaccept = 1;
else if (errno != ECONNABORTED && errno != EWOULDBLOCK)
perror("accept");
return;
}
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
perror("fcntl(sock, O_NONBLOCK)");
file_log(f->file);
log_puts(": failed to set non-blocking mode\n");
close(sock);
return;
}
@ -257,7 +264,8 @@ listen_in(void *arg)
opt = 1;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
&opt, sizeof(int)) < 0) {
perror("setsockopt");
file_log(f->file);
log_puts(": failed to set TCP_NODELAY flag\n");
close(sock);
return;
}

View File

@ -24,12 +24,13 @@ struct listen {
struct file *file;
char *path;
int fd;
int slowaccept;
};
extern struct listen *listen_list;
void listen_new_un(char *);
void listen_new_tcp(char *, unsigned int);
int listen_new_un(char *);
int listen_new_tcp(char *, unsigned int);
int listen_init(struct listen *);
void listen_close(struct listen *);

View File

@ -250,7 +250,7 @@ midi_tickets(struct midi *iep)
}
/*
* in the worst case output message is twice the
* in the worst case output message is twice the
* input message (2-byte messages with running status)
*/
tickets = maxavail / 2 - iep->tickets;
@ -333,14 +333,14 @@ midi_in(struct midi *iep, unsigned char *idata, int icount)
* store the given message in the output buffer
*/
void
midi_out(struct midi *oep, unsigned char *idata, int icount)
midi_out(struct midi *oep, unsigned char *idata, int icount)
{
unsigned char *odata;
int ocount;
#ifdef DEBUG
int i;
#endif
while (icount > 0) {
if (oep->obuf.used == oep->obuf.len) {
#ifdef DEBUG
@ -376,13 +376,11 @@ midi_out(struct midi *oep, unsigned char *idata, int icount)
}
}
#ifdef DEBUG
void
port_log(struct port *p)
{
midi_log(p->midi);
}
#endif
void
port_imsg(void *arg, unsigned char *msg, int size)
@ -427,18 +425,16 @@ port_exit(void *arg)
struct port *
port_new(char *path, unsigned int mode, int hold)
{
struct port *c, **pc;
struct port *c;
c = xmalloc(sizeof(struct port));
c->path = xstrdup(path);
c->state = PORT_CFG;
c->hold = hold;
c->midi = midi_new(&port_midiops, c, mode);
midi_portnum++;
for (pc = &port_list; *pc != NULL; pc = &(*pc)->next)
; /* nothing */
c->next = NULL;
*pc = c;
c->num = midi_portnum++;
c->next = port_list;
port_list = c;
return c;
}
@ -504,7 +500,7 @@ port_bynum(int num)
struct port *p;
for (p = port_list; p != NULL; p = p->next) {
if (num-- == 0)
if (p->num == num)
return p;
}
return NULL;
@ -536,9 +532,9 @@ port_close(struct port *c)
panic();
}
#endif
c->state = PORT_CFG;
c->state = PORT_CFG;
port_mio_close(c);
for (i = 0; i < MIDI_NEP; i++) {
ep = midi_ep + i;
if ((ep->txmask & c->midi->self) ||

View File

@ -73,7 +73,7 @@ struct midi {
unsigned int len; /* expected ``msg'' length */
unsigned int txmask; /* list of ep we send to */
unsigned int self; /* equal (1 << index) */
unsigned int tickets; /* max bytes we can process */
int tickets; /* max bytes we can process */
struct abuf obuf; /* output buffer */
};
@ -87,6 +87,7 @@ struct port {
#define PORT_INIT 1
#define PORT_DRAIN 2
unsigned int state;
unsigned int num; /* port serial number */
char *path; /* hold the port open ? */
int hold;
struct midi *midi;

View File

@ -14,8 +14,6 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dev.h"
@ -32,20 +30,27 @@ opt_new(char *name, struct dev *dev,
int pmin, int pmax, int rmin, int rmax,
int maxweight, int mmc, int dup, unsigned int mode)
{
struct opt *o, **po;
struct opt *o;
unsigned int len;
char c;
if (opt_byname(name, dev->num)) {
log_puts(name);
log_puts(": already defined\n");
return NULL;
}
for (len = 0; name[len] != '\0'; len++) {
if (len == OPT_NAMEMAX) {
fprintf(stderr, "%s: name too long\n", name);
exit(1);
log_puts(name);
log_puts(": too long\n");
return NULL;
}
c = name[len];
if ((c < 'a' || c > 'z') &&
(c < 'A' || c > 'Z')) {
fprintf(stderr, "%s: '%c' not allowed\n", name, c);
exit(1);
log_puts(name);
log_puts(": only alphabetic chars allowed\n");
return NULL;
}
}
o = xmalloc(sizeof(struct opt));
@ -63,15 +68,8 @@ opt_new(char *name, struct dev *dev,
o->mode = mode;
o->dev = dev;
memcpy(o->name, name, len + 1);
for (po = &opt_list; *po != NULL; po = &(*po)->next) {
if (o->dev->num == (*po)->dev->num &&
strcmp(o->name, (*po)->name) == 0) {
fprintf(stderr, "%s: already defined\n", o->name);
exit(1);
}
}
o->next = NULL;
*po = o;
o->next = opt_list;
opt_list = o;
if (log_level >= 2) {
dev_log(o->dev);
log_puts(".");

View File

@ -163,7 +163,7 @@ dev_sio_open(struct dev *d)
log_putu(par.pchan);
log_puts(": unsupported number of play channels\n");
goto bad_close;
}
}
if ((mode & SIO_REC) && par.rchan > NCHAN_MAX) {
log_puts(d->path);
log_puts(": ");
@ -296,7 +296,7 @@ dev_sio_pollfd(void *arg, struct pollfd *pfd)
{
struct dev *d = arg;
int events;
events = (d->sio.cstate == DEV_SIO_READ) ? POLLIN : POLLOUT;
return sio_pollfd(d->sio.hdl, pfd, events);
}

View File

@ -1,532 +0,0 @@
.\" $OpenBSD$
.\"
.\" Copyright (c) 2006-2012 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.
.\"
.Dd $Mdocdate$
.Dt SNDIOD 1
.Os
.Sh NAME
.Nm sndiod
.Nd audio/MIDI server
.Sh SYNOPSIS
.Nm sndiod
.Bk -words
.Op Fl d
.Op Fl a Ar flag
.Op Fl b Ar nframes
.Op Fl C Ar min : Ns Ar max
.Op Fl c Ar min : Ns Ar max
.Op Fl e Ar enc
.Op Fl f Ar device
.Op Fl j Ar flag
.Op Fl L Ar addr
.Op Fl m Ar mode
.Op Fl q Ar port
.Op Fl r Ar rate
.Op Fl s Ar name
.Op Fl t Ar mode
.Op Fl U Ar unit
.Op Fl v Ar volume
.Op Fl w Ar flag
.Op Fl z Ar nframes
.Ek
.Sh DESCRIPTION
The
.Nm
daemon is an intermediate layer between
audio or MIDI programs and the hardware.
It performs the necessary audio processing to
allow any program to work on any supported hardware.
By default,
.Nm
accepts connections from programs
running on the same system only;
it initializes only when programs are using its services,
allowing
.Nm
to consume a negligible amount of system resources the rest of the time.
Systems with no audio hardware can use
.Nm
to keep hot-pluggable devices usable by default at
virtually no cost.
.Pp
.Nm
operates as follows: it exposes at least one
.Em sub-device
that any number of audio programs can connect to and use as if it was
audio hardware.
During playback,
.Nm
receives audio data concurrently from all programs, mixes it and sends
the result to the hardware device.
Similarly, during recording it duplicates audio data recorded
from the device and sends it to all programs.
Since audio data flows through the
.Nm
process, it has the opportunity to process audio data on the fly:
.Pp
.Bl -bullet -offset indent -compact
.It
Change the sound encoding to overcome incompatibilities between
software and hardware.
.It
Route the sound from one channel to another,
join stereo or split mono.
.It
Control the per-application playback volume as well as the
master volume.
.It
Monitor the sound being played, allowing one program to record
what other programs play.
.El
.Pp
Processing is configured on a per sub-device basis, meaning that
the sound of all programs connected to the same sub-device will be
processed according to the same configuration.
Multiple sub-devices can be defined, allowing multiple configurations
to coexist.
The user selects the configuration a given program will use
by selecting the sub-device the program uses.
.Pp
.Nm
exposes MIDI thru boxes (hubs),
allowing programs to send MIDI messages to each other
or to hardware MIDI ports in a uniform way.
.Pp
Finally,
.Nm
exposes a control MIDI port usable for:
.Pp
.Bl -bullet -offset indent -compact
.It
Volume control.
.It
Common clock source for audio and MIDI programs.
.It
Start, stop and relocate groups of audio programs.
.El
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl a Ar flag
Control whether
.Nm
opens the audio device or the MIDI port only when needed or keeps
it open all the time.
If the flag is
.Va on
then the audio device or MIDI port is kept open all the time, ensuring
no other program can steal it.
If the flag is
.Va off ,
then it's automatically closed, allowing other programs to have direct
access to the audio device, or the device to be disconnected.
The default is
.Va off .
.It Fl b Ar nframes
The buffer size of the audio device in frames.
A frame consists of one sample for each channel in the stream.
This is the number of frames that will be buffered before being played
and thus controls the playback latency.
The default is 7680 or twice the block size
.Pq Fl z ,
if the block size is set.
.It Xo
.Fl C Ar min : Ns Ar max ,
.Fl c Ar min : Ns Ar max
.Xc
The range of channel numbers for recording and playback directions,
respectively any client is allowed to use.
This is a subset of the audio device channels.
The default is 0:1, i.e. stereo.
.It Fl d
Increase log verbosity.
.Nm
logs on
.Em stderr .
.It Fl e Ar enc
Attempt to configure the device to use this encoding.
The default is
.Va s16 .
Encoding names use the follwing scheme: signedness
.Po
.Va s
or
.Va u
.Pc
followed
by the precision in bits, the byte-order
.Po
.Va le
or
.Va be
.Pc ,
the number of
bytes per sample, and the alignment
.Po
.Va msb
or
.Va lsb
.Pc .
Only the signedness and the precision are mandatory.
Examples:
.Va u8 , s16le , s24le3 , s24le4lsb .
.It Fl f Ar device
Add this
.Xr sndio 7
audio device to devices used for playing and/or recording.
Preceding per-device options
.Pq Fl aberwz
apply to this device.
Sub-devices
.Pq Fl s
that are applied after will be attached to this device.
Device mode and parameters are determined from sub-devices
attached to it.
.It Fl j Ar flag
Control whether program channels are joined or expanded if
the number of channels requested by a program is not equal
to the device number of channels.
If the flag is
.Va off
then client channels are routed to the corresponding
device channel, possibly discarding channels not present in the device.
If the flag is
.Va on ,
then a single client channel may be sent on multiple device channels,
or multiple client channels may be sent to a single device channel.
For instance, this feature could be used for mono to stereo conversions.
The default is
.Ar on .
.It Fl L Ar addr
Specify a local network address
.Nm
should listen;
.Nm
will listen on TCP port 11025+n, where n is the unit number
specified with
.Fl U .
Without this option,
.Nm
listens on the
.Ux Ns -domain
socket only, and is not reachable from any network.
If the option argument is
.Sq -
then
.Nm
will accept connections from any address.
.It Fl m Ar mode
Set the sub-device mode.
Valid modes are
.Ar play ,
.Ar rec ,
and
.Ar mon ,
corresponding to playback, recording and monitoring.
A monitoring stream is a fake recording stream corresponding to
the mix of all playback streams.
Multiple modes can be specified, separated by commas,
but the same sub-device cannot be used for both recording and monitoring.
The default is
.Ar play , Ns Ar rec
(i.e. full-duplex).
.It Fl q Ar port
Expose the given MIDI port.
This allows multiple programs to share the port.
.It Fl r Ar rate
Attempt to force the device to use this sample rate in Hertz.
The default is 48000.
.It Fl s Ar name
Add
.Ar name
to the list of sub-devices to expose.
This allows clients to use
.Nm
instead of the physical audio device for audio input and output
in order to share the physical device with other clients.
Defining multiple sub-devices allows splitting a physical audio device
into sub-devices having different properties (e.g. channel ranges).
The given
.Ar name
corresponds to the
.Dq option
part of the
.Xr sndio 7
device name string.
.It Fl t Ar mode
Select the way clients are controlled by MIDI Machine Control (MMC)
messages received by
.Nm .
If the mode is
.Va off
(the default), then programs are not affected by MMC messages.
If the mode is
.Va slave ,
then programs are started synchronously by MMC start messages;
additionally, the server clock is exposed as MIDI Time Code (MTC)
messages allowing MTC-capable software or hardware to be synchronized
to audio programs.
.It Fl U Ar unit
Unit number.
Each
.Nm
server instance has an unique unit number,
used in
.Xr sndio 7
device names.
The default is 0.
The unit number must be set before any
.Fl L
is used.
.It Fl v Ar volume
Software volume attenuation of playback.
The value must be between 1 and 127,
corresponding to \-42dB and \-0dB attenuation in 1/3dB steps.
Clients inherit this parameter.
Reducing the volume in advance allows a client's volume to stay independent
from the number of clients as long as their number is small enough.
18 volume units (i.e. \-6dB attenuation) allows the number
of playback programs to be doubled.
The default is 118 i.e. \-3dB.
.It Fl w Ar flag
Control
.Nm
behaviour when the maximum volume of the hardware is reached
and a new program starts playing.
This happens only when volumes are not properly set using the
.Fl v
option.
If the flag is
.Va on ,
then the master volume is automatically adjusted to avoid clipping.
Using
.Va off
makes sense in the rare situation where all programs lower their volumes.
The default is
.Va on .
.It Fl z Ar nframes
The audio device block size in frames.
This is the number of frames between audio clock ticks,
i.e. the clock resolution.
If a sub-device is created with the
.Fl t
option, and MTC is used for synchronization, the clock
resolution must be 96, 100 or 120 ticks per second for maximum
accuracy.
For instance, 100 ticks per second at 48000Hz corresponds
to a 480 frame block size.
The default is 960 or half of the buffer size
.Pq Fl b ,
if the buffer size is set.
.El
.Pp
On the command line,
per-device parameters
.Pq Fl aberwz
must precede the device definition
.Pq Fl f ,
and per-sub-device parameters
.Pq Fl Ccjmtvx
must precede the sub-device definition
.Pq Fl s .
Sub-device definitions
.Pq Fl s
must follow the definition of the device
.Pq Fl f
to which they are attached.
.Pp
If no audio devices
.Pq Fl f
are specified, first 4 audio devices are added and
settings are applied to all of them.
Similarly, if no MIDI ports
.Pq Fl q
are specified, first 8 MIDI ports are added and
settings are applied to all of them.
If no sub-devices
.Pq Fl s
are specified for a device, a default sub-device is
created attached to it.
If a device or MIDI port
.Pq Fl fq
is defined twice, both definitions are merged:
parameters of the first one are used but sub-devices
.Pq Fl s
of both definitions are created.
.Pp
If
.Nm
is sent
.Dv SIGHUP ,
.Dv SIGINT
or
.Dv SIGTERM ,
it terminates.
.Pp
By default, when the program cannot accept
recorded data fast enough or cannot provide data to play fast enough,
the program is paused, i.e. samples that cannot be written are discarded
and samples that cannot be read are replaced by silence.
If a sub-device is created with the
.Fl t
option, then recorded samples are discarded,
but the same amount of silence will be written
once the program is unblocked, in order to reach the right position in time.
Similarly silence is played, but the same amount of samples will be discarded
once the program is unblocked.
This ensures proper synchronization between programs.
.Sh MIDI CONTROL
.Nm
creates a MIDI port with the same name as the exposed audio
sub-device to which MIDI programs can connect.
.Nm
exposes the audio device clock
and allows audio device properties to be controlled
through MIDI.
.Pp
A MIDI channel is assigned to each stream, and the volume
is changed using the standard volume controller (number 7).
Similarly, when the audio client changes its volume,
the same MIDI controller message is sent out; it can be used
for instance for monitoring or as feedback for motorized
faders.
.Pp
The master volume can be changed using the standard master volume
system exclusive message.
.Pp
Streams created with the
.Fl t
option are controlled by the following MMC messages:
.Bl -tag -width relocateXXX -offset indent
.It relocate
This message is ignored by audio
.Nm
clients, but the given time position is sent to MIDI ports as an MTC
.Dq "full frame"
message forcing all MTC-slaves to relocate to the given
position (see below).
.It start
Put all streams in starting mode.
In this mode,
.Nm
waits for all streams to become ready
to start, and then starts them synchronously.
Once started, new streams can be created
.Pq Nm sndiod
but they will be blocked
until the next stop-to-start transition.
.It stop
Put all streams in stopped mode (the default).
In this mode, any stream attempting to start playback or recording
is paused.
Client streams that are already
started are not affected until they stop and try to start again.
.El
.Pp
Streams created with the
.Fl t
option export the
.Nm
device clock using MTC, allowing non-audio
software or hardware to be synchronized to the audio stream.
Maximum accuracy is achieved when the number of blocks per
second is equal to one of the standard MTC clock rates (96, 100 and 120Hz).
The following sample rates
.Pq Fl r
and block sizes
.Pq Fl z
are recommended:
.Pp
.Bl -bullet -offset indent -compact
.It
44100Hz, 441 frames (MTC rate is 100Hz)
.It
48000Hz, 400 frames (MTC rate is 120Hz)
.It
48000Hz, 480 frames (MTC rate is 100Hz)
.It
48000Hz, 500 frames (MTC rate is 96Hz)
.El
.Pp
For instance, the following command will create two devices:
the default
.Va snd/0
and a MIDI-controlled
.Va snd/0.mmc :
.Bd -literal -offset indent
$ sndiod -r 48000 -z 400 -s default -t slave -s mmc
.Ed
.Pp
Streams connected to
.Va snd/0
behave normally, while streams connected to
.Va snd/0.mmc
wait for the MMC start signal and start synchronously.
Regardless of which device a stream is connected to,
its playback volume knob is exposed.
.Sh EXAMPLES
Start server using default parameters, creating an
additional sub-device for output to channels 2:3 only (rear speakers
on most cards), exposing the
.Pa snd/0
and
.Pa snd/0.rear
devices:
.Bd -literal -offset indent
$ sndiod -s default -c 2:3 -s rear
.Ed
.Pp
Start server creating the default sub-device with low volume and
an additional sub-device for high volume output, exposing the
.Pa snd/0
and
.Pa snd/0.max
devices:
.Bd -literal -offset indent
$ sndiod -v 65 -s default -v 127 -s max
.Ed
.Pp
Start server configuring the audio device to use
a 48kHz sample frequency, 240-frame block size,
and 2-block buffers.
The corresponding latency is 10ms, which is
the time it takes the sound to propagate 3.5 meters.
.Bd -literal -offset indent
$ sndiod -r 48000 -b 480 -z 240
.Ed
.Sh SEE ALSO
.Xr sndio 7
.Sh BUGS
Resampling is low quality; down-sampling especially should be avoided
when recording.
.Pp
Processing is done using 16-bit arithmetic,
thus samples with more than 16 bits are rounded.
16 bits (i.e. 97dB dynamic) are largely enough for most applications though.
Processing precision can be increased to 24-bit at compilation time though.
.Pp
If
.Fl a Ar off
is used,
.Nm
creates sub-devices to expose first
and then opens the audio hardware on demand.
Technically, this allows
.Nm
to attempt to use one of the sub-devices it exposes as an audio device,
creating a deadlock.
There's nothing to prevent the user
from shooting himself in the foot by creating such a deadlock.

View File

@ -14,7 +14,6 @@
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/queue.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/resource.h>
@ -95,9 +94,9 @@ unsigned int opt_mode(void);
void getbasepath(char *, size_t);
void setsig(void);
void unsetsig(void);
void privdrop(void);
struct dev *mkdev(char *, struct aparams *,
int, int, int, int, int, int);
struct port *mkport(char *, int);
struct opt *mkopt(char *, struct dev *,
int, int, int, int, int, int, int, int);
@ -244,11 +243,11 @@ unsetsig(void)
sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_DFL;
if (sigaction(SIGHUP, &sa, NULL) < 0)
err(1, "unsetsig(hup): sigaction failed\n");
err(1, "unsetsig(hup): sigaction failed");
if (sigaction(SIGTERM, &sa, NULL) < 0)
err(1, "unsetsig(term): sigaction failed\n");
err(1, "unsetsig(term): sigaction failed");
if (sigaction(SIGINT, &sa, NULL) < 0)
err(1, "unsetsig(int): sigaction failed\n");
err(1, "unsetsig(int): sigaction failed");
}
void
@ -256,7 +255,7 @@ getbasepath(char *base, size_t size)
{
uid_t uid;
struct stat sb;
mode_t mask;
mode_t mask, omask;
uid = geteuid();
if (uid == 0) {
@ -266,31 +265,20 @@ getbasepath(char *base, size_t size)
mask = 077;
snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR "-%u", uid);
}
if (mkdir(base, 0777 & ~mask) < 0) {
omask = umask(mask);
if (mkdir(base, 0777) < 0) {
if (errno != EEXIST)
err(1, "mkdir(\"%s\")", base);
}
umask(omask);
if (stat(base, &sb) < 0)
err(1, "stat(\"%s\")", base);
if (!S_ISDIR(sb.st_mode))
errx(1, "%s is not a directory", base);
if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
errx(1, "%s has wrong permissions", base);
}
void
privdrop(void)
{
struct passwd *pw;
if ((pw = getpwnam(SNDIO_USER)) == NULL)
errx(1, "unknown user %s", SNDIO_USER);
if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
err(1, "setpriority");
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
err(1, "cannot drop privileges");
}
struct dev *
mkdev(char *path, struct aparams *par,
int mode, int bufsz, int round, int rate, int hold, int autovol)
@ -339,7 +327,7 @@ mkopt(char *path, struct dev *d,
o = opt_new(path, d, pmin, pmax, rmin, rmax,
MIDI_TO_ADATA(vol), mmc, dup, mode);
if (o == NULL)
errx(1, "%s: couldn't create subdev", path);
return NULL;
dev_adjpar(d, o->mode, o->pmin, o->pmax, o->rmin, o->rmax);
return o;
}
@ -350,14 +338,18 @@ main(int argc, char **argv)
int c, background, unit;
int pmin, pmax, rmin, rmax;
char base[SOCKPATH_MAX], path[SOCKPATH_MAX];
char loc[32];
unsigned int mode, dup, mmc, vol, i;
unsigned int mode, dup, mmc, vol;
unsigned int hold, autovol, bufsz, round, rate;
const char *str;
struct aparams par;
struct dev *d;
struct port *p;
struct listen *l;
struct passwd *pw;
struct tcpaddr {
char *host;
struct tcpaddr *next;
} *tcpaddr_list, *ta;
atexit(log_flush);
@ -380,25 +372,27 @@ main(int argc, char **argv)
rmax = 1;
aparams_init(&par);
mode = MODE_PLAY | MODE_REC;
tcpaddr_list = NULL;
setsig();
filelist_init();
while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:Mq:r:s:t:U:v:w:x:z:")) != -1) {
while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:q:r:s:t:U:v:w:x:z:")) != -1) {
switch (c) {
case 'd':
log_level++;
background = 0;
break;
case 'U':
if (listen_list)
errx(1, "-U must come before -L");
unit = strtonum(optarg, 0, 15, &str);
if (str)
errx(1, "%s: unit number is %s", optarg, str);
break;
case 'L':
listen_new_tcp(optarg, AUCAT_PORT + unit);
ta = xmalloc(sizeof(struct tcpaddr));
ta->host = optarg;
ta->next = tcpaddr_list;
tcpaddr_list = ta;
break;
case 'm':
mode = opt_mode();
@ -433,8 +427,9 @@ main(int argc, char **argv)
d = mkdev(DEFAULT_DEV, &par, 0, bufsz, round,
rate, hold, autovol);
}
mkopt(optarg, d, pmin, pmax, rmin, rmax,
mode, vol, mmc, dup);
if (mkopt(optarg, d, pmin, pmax, rmin, rmax,
mode, vol, mmc, dup) == NULL)
return 1;
break;
case 'q':
mkport(optarg, hold);
@ -470,29 +465,33 @@ main(int argc, char **argv)
fputs(usagestr, stderr);
return 1;
}
if (dev_list == NULL) {
for (i = 0; i < 4; i++) {
snprintf(loc, sizeof(loc), "rsnd/%u", i);
mkdev(loc, &par, 0, bufsz, round, rate, hold, autovol);
}
}
if (port_list == NULL) {
for (i = 0; i < 8; i++) {
snprintf(loc, sizeof(loc), "rmidi/%u", i);
mkport(loc, hold);
}
}
if (dev_list == NULL)
mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol);
for (d = dev_list; d != NULL; d = d->next) {
if (opt_byname("default", d->num))
continue;
mkopt("default", d, pmin, pmax, rmin, rmax,
mode, vol, mmc, dup);
if (mkopt("default", d, pmin, pmax, rmin, rmax,
mode, vol, mmc, dup) == NULL)
return 1;
}
getbasepath(base, sizeof(base));
snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit);
listen_new_un(path);
if (geteuid() == 0)
privdrop();
if (!listen_new_un(path))
return 1;
for (ta = tcpaddr_list; ta != NULL; ta = ta->next) {
if (!listen_new_tcp(ta->host, AUCAT_PORT + unit))
return 1;
}
if (geteuid() == 0) {
if ((pw = getpwnam(SNDIO_USER)) == NULL)
errx(1, "unknown user %s", SNDIO_USER);
if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
err(1, "setpriority");
if (setgroups(1, &pw->pw_gid) ||
setgid(pw->pw_gid) ||
setuid(pw->pw_uid))
err(1, "cannot drop privileges");
}
midi_init();
for (p = port_list; p != NULL; p = p->next) {
if (!port_init(p))
@ -512,10 +511,6 @@ main(int argc, char **argv)
if (daemon(0, 0) < 0)
err(1, "daemon");
}
/*
* Loop, start audio.
*/
for (;;) {
if (quit_flag)
break;
@ -539,8 +534,13 @@ main(int argc, char **argv)
dev_del(dev_list);
while (port_list)
port_del(port_list);
filelist_done();
while (tcpaddr_list) {
ta = tcpaddr_list;
tcpaddr_list = ta->next;
xfree(ta);
}
rmdir(base);
filelist_done();
unsetsig();
return 0;
}

View File

@ -163,6 +163,7 @@ sock_close(struct sock *f)
}
file_del(f->file);
close(f->fd);
file_slowaccept = 0;
xfree(f);
}
@ -350,7 +351,7 @@ sock_fdwrite(struct sock *f, void *data, int count)
}
sock_close(f);
} else {
#ifdef DEBUG
#ifdef DEBUG
if (log_level >= 4) {
sock_log(f);
log_puts(": write blocked\n");
@ -391,7 +392,7 @@ sock_fdread(struct sock *f, void *data, int count)
}
sock_close(f);
} else {
#ifdef DEBUG
#ifdef DEBUG
if (log_level >= 4) {
sock_log(f);
log_puts(": read blocked\n");
@ -1143,7 +1144,7 @@ sock_execmsg(struct sock *f)
f->ralign = s->round * s->mix.bpf;
}
}
slot_stop(s);
slot_stop(s);
break;
case AMSG_SETPAR:
#ifdef DEBUG
@ -1241,7 +1242,7 @@ sock_execmsg(struct sock *f)
f->rtodo = sizeof(struct amsg);
f->rstate = SOCK_RMSG;
f->lastvol = ctl; /* dont trigger feedback message */
slot_setvol(f->slot, ctl);
slot_setvol(s, ctl);
dev_midi_vol(s->dev, s);
dev_onctl(s->dev, s->dev->ctl_addr +
CTLADDR_SLOT_LEVEL(f->slot - s->dev->slot), ctl);
@ -1439,7 +1440,7 @@ sock_buildmsg(struct sock *f)
if (f->fillpending > 0) {
AMSG_INIT(&f->wmsg);
f->wmsg.cmd = htonl(AMSG_FLOWCTL);
f->wmsg.cmd = htonl(AMSG_FLOWCTL);
f->wmsg.u.ts.delta = htonl(f->fillpending);
size = f->fillpending;
if (f->slot)
@ -1483,7 +1484,7 @@ sock_buildmsg(struct sock *f)
}
if (f->midi != NULL && f->midi->obuf.used > 0) {
size = f->midi->obuf.used;
size = f->midi->obuf.used;
if (size > AMSG_DATAMAX)
size = AMSG_DATAMAX;
AMSG_INIT(&f->wmsg);

View File

@ -91,7 +91,7 @@ log_putx(unsigned long num)
c += (c < 10) ? '0' : 'a' - 10;
LOG_PUTC(c);
}
} else
} else
LOG_PUTC('0');
}
@ -146,7 +146,7 @@ void *
xmalloc(size_t size)
{
void *p;
p = malloc(size);
if (p == NULL) {
log_puts("failed to allocate ");
@ -163,6 +163,12 @@ xmalloc(size_t size)
void
xfree(void *p)
{
#ifdef DEBUG
if (p == NULL) {
log_puts("xfree with NULL arg\n");
panic();
}
#endif
free(p);
}