#include <sys/queue.h>
#include <sys/soundcard.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <sndio.h>
#include "backend.h"
#include "int.h"
static struct sio_hdl *
get_sio_hdl(struct voss_backend *pbe)
{
if (pbe)
return (pbe->arg);
return (NULL);
}
static void
sndio_close(struct voss_backend *pbe)
{
if (!pbe)
return;
if (get_sio_hdl(pbe))
sio_close(get_sio_hdl(pbe));
}
static int
sndio_get_signedness(int *fmt)
{
int s_fmt = *fmt & (VPREFERRED_SLE_AFMT | VPREFERRED_SBE_AFMT);
if (s_fmt) {
*fmt = s_fmt;
return (1);
}
*fmt = *fmt & (VPREFERRED_ULE_AFMT | VPREFERRED_UBE_AFMT);
return (0);
}
static int
sndio_get_endianness_is_le(int *fmt)
{
int le_fmt = *fmt & (VPREFERRED_SLE_AFMT | VPREFERRED_ULE_AFMT);
if (le_fmt) {
*fmt = le_fmt;
return (1);
}
*fmt = *fmt & (VPREFERRED_SBE_AFMT | VPREFERRED_UBE_AFMT);
return (0);
}
static int
sndio_get_bits(int *fmt)
{
if (*fmt & AFMT_16BIT)
return (16);
if (*fmt & AFMT_24BIT)
return (24);
if (*fmt & AFMT_32BIT)
return (32);
if (*fmt & AFMT_8BIT)
return (8);
return (-1);
}
static int
sndio_open(struct voss_backend *pbe, const char *devname,
int samplerate, int bufsize, int *pchannels, int *pformat, int direction)
{
const char *sndio_name = devname + strlen("/dev/sndio/");
int sig = sndio_get_signedness(pformat);
int le = sndio_get_endianness_is_le(pformat);
int bits = sndio_get_bits(pformat);
if (bits == -1) {
warn("unsupported format precision");
return (-1);
}
struct sio_hdl *hdl = sio_open(sndio_name, direction, 0);
if (hdl == 0) {
warn("sndio: failed to open device");
return (-1);
}
struct sio_par par;
sio_initpar(&par);
par.pchan = *pchannels;
par.sig = sig;
par.bits = bits;
par.bps = SIO_BPS(bits);
par.le = le;
par.rate = samplerate;
par.appbufsz = bufsize;
par.xrun = SIO_SYNC;
if (!sio_setpar(hdl, &par))
errx(1, "internal error, sio_setpar() failed");
if (!sio_getpar(hdl, &par))
errx(1, "internal error, sio_getpar() failed");
if ((int)par.pchan != *pchannels)
errx(1, "couldn't set number of channels");
if ((int)par.sig != sig || (int)par.bits != bits || (int)par.le != le)
errx(1, "couldn't set format");
if ((int)par.bits != bits)
errx(1, "couldn't set precision");
if ((int)par.rate < samplerate * 995 / 1000 ||
(int)par.rate > samplerate * 1005 / 1000)
errx(1, "couldn't set rate");
if (par.xrun != SIO_SYNC)
errx(1, "couldn't set xun policy");
pbe->arg = hdl;
if (!sio_start(hdl))
errx(1, "couldn't start device");
return (0);
}
static int
sndio_open_play(struct voss_backend *pbe, const char *devname,
int samplerate, int bufsize, int *pchannels, int *pformat)
{
return (sndio_open(pbe, devname, samplerate, bufsize, pchannels, pformat, SIO_PLAY));
}
static int
sndio_open_rec(struct voss_backend *pbe, const char *devname,
int samplerate, int bufsize, int *pchannels, int *pformat)
{
return (sndio_open(pbe, devname, samplerate, bufsize, pchannels, pformat, SIO_REC));
}
static int
sndio_play_transfer(struct voss_backend *pbe, void *ptr, int len)
{
return (sio_write(get_sio_hdl(pbe), ptr, len));
}
static int
sndio_rec_transfer(struct voss_backend *pbe, void *ptr, int len)
{
return (sio_read(get_sio_hdl(pbe), ptr, len));
}
static void
sndio_delay(struct voss_backend *pbe __unused, int *pdelay)
{
*pdelay = -1;
}
struct voss_backend voss_backend_sndio_rec = {
.open = sndio_open_rec,
.close = sndio_close,
.transfer = sndio_rec_transfer,
.delay = sndio_delay,
.fd = -1,
};
struct voss_backend voss_backend_sndio_play = {
.open = sndio_open_play,
.close = sndio_close,
.transfer = sndio_play_transfer,
.delay = sndio_delay,
.fd = -1,
};