878 lines
24 KiB
Diff
878 lines
24 KiB
Diff
diff --git Makefile.in Makefile.in
|
|
index 5e1a764..2747f73 100644
|
|
--- Makefile.in
|
|
+++ Makefile.in
|
|
@@ -44,7 +44,7 @@ PALIB = libportaudio.la
|
|
PAINC = include/portaudio.h
|
|
|
|
PA_LDFLAGS = $(LDFLAGS) $(SHARED_FLAGS) -rpath $(libdir) -no-undefined \
|
|
- -export-symbols-regex "(Pa|PaMacCore|PaJack|PaAlsa|PaAsio|PaOSS)_.*" \
|
|
+ -export-symbols-regex "(Pa|PaMacCore|PaJack|PaAlsa|PaAsio|PaOSS|PaSndio)_.*" \
|
|
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
|
|
|
|
COMMON_OBJS = \
|
|
@@ -146,6 +146,7 @@ SRC_DIRS = \
|
|
src/hostapi/dsound \
|
|
src/hostapi/jack \
|
|
src/hostapi/oss \
|
|
+ src/hostapi/sndio \
|
|
src/hostapi/wasapi \
|
|
src/hostapi/wdmks \
|
|
src/hostapi/wmme \
|
|
diff --git configure.in configure.in
|
|
index 13816fb..4c06d10 100644
|
|
--- configure.in
|
|
+++ configure.in
|
|
@@ -24,6 +24,10 @@ AC_ARG_WITH(alsa,
|
|
AS_HELP_STRING([--with-alsa], [Enable support for ALSA @<:@autodetect@:>@]),
|
|
[with_alsa=$withval])
|
|
|
|
+AC_ARG_WITH(sndio,
|
|
+ AS_HELP_STRING([--with-sndio], [Enable support for sndio @<:@autodetect@:>@]),
|
|
+ [with_sndio=$withval])
|
|
+
|
|
AC_ARG_WITH(jack,
|
|
AS_HELP_STRING([--with-jack], [Enable support for JACK @<:@autodetect@:>@]),
|
|
[with_jack=$withval])
|
|
@@ -120,6 +124,10 @@ have_alsa=no
|
|
if test "x$with_alsa" != "xno"; then
|
|
AC_CHECK_LIB(asound, snd_pcm_open, have_alsa=yes, have_alsa=no)
|
|
fi
|
|
+have_sndio=no
|
|
+if test "x$with_sndio" != "xno"; then
|
|
+ AC_CHECK_LIB(sndio, sio_open, have_sndio=yes, have_sndio=no)
|
|
+fi
|
|
have_asihpi=no
|
|
if test "x$with_asihpi" != "xno"; then
|
|
AC_CHECK_LIB(hpi, HPI_SubSysCreate, have_asihpi=yes, have_asihpi=no, -lm)
|
|
@@ -406,6 +414,13 @@ case "${host_os}" in
|
|
AC_DEFINE(PA_USE_ALSA,1)
|
|
fi
|
|
|
|
+ if [[ "$have_sndio" = "yes" -a "$with_sndio" != "no" ]] ; then
|
|
+ DLL_LIBS="$DLL_LIBS -lsndio"
|
|
+ LIBS="$LIBS -lsndio"
|
|
+ OTHER_OBJS="$OTHER_OBJS src/hostapi/sndio/pa_sndio.o"
|
|
+ AC_DEFINE(PA_USE_SNDIO,1)
|
|
+ fi
|
|
+
|
|
if [[ "$have_jack" = "yes" ] && [ "$with_jack" != "no" ]] ; then
|
|
DLL_LIBS="$DLL_LIBS $JACK_LIBS"
|
|
CFLAGS="$CFLAGS $JACK_CFLAGS"
|
|
@@ -509,6 +524,7 @@ case "$target_os" in
|
|
;;
|
|
*)
|
|
AC_MSG_RESULT([
|
|
+ Sndio ....................... $have_sndio
|
|
OSS ......................... $have_oss
|
|
JACK ........................ $have_jack
|
|
])
|
|
diff --git include/portaudio.h include/portaudio.h
|
|
index 8a94aaf..f94d9c4 100644
|
|
--- include/portaudio.h
|
|
+++ include/portaudio.h
|
|
@@ -287,7 +287,8 @@ typedef enum PaHostApiTypeId
|
|
paWDMKS=11,
|
|
paJACK=12,
|
|
paWASAPI=13,
|
|
- paAudioScienceHPI=14
|
|
+ paAudioScienceHPI=14,
|
|
+ paSndio=15
|
|
} PaHostApiTypeId;
|
|
|
|
|
|
diff --git src/hostapi/sndio/pa_sndio.c src/hostapi/sndio/pa_sndio.c
|
|
new file mode 100644
|
|
index 0000000..725ef47
|
|
--- /dev/null
|
|
+++ src/hostapi/sndio/pa_sndio.c
|
|
@@ -0,0 +1,765 @@
|
|
+/*
|
|
+ * Copyright (c) 2009 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.
|
|
+ */
|
|
+#include <sys/types.h>
|
|
+#include <pthread.h>
|
|
+#include <poll.h>
|
|
+#include <errno.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdio.h>
|
|
+#include <sndio.h>
|
|
+
|
|
+#include "pa_util.h"
|
|
+#include "pa_hostapi.h"
|
|
+#include "pa_stream.h"
|
|
+#include "pa_process.h"
|
|
+#include "pa_allocation.h"
|
|
+
|
|
+#if 0
|
|
+#define DPR(...) do { fprintf(stderr, __VA_ARGS__); } while (0)
|
|
+#else
|
|
+#define DPR(...) do {} while (0)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * per-stream data
|
|
+ */
|
|
+typedef struct PaSndioStream
|
|
+{
|
|
+ PaUtilStreamRepresentation base;
|
|
+ PaUtilBufferProcessor bufproc; /* format conversion */
|
|
+ struct sio_hdl *hdl; /* handle for device i/o */
|
|
+ struct sio_par par; /* current device parameters */
|
|
+ unsigned mode; /* SIO_PLAY, SIO_REC or both */
|
|
+ int stopped; /* stop requested or not started */
|
|
+ int active; /* thread is running */
|
|
+ unsigned long long realpos; /* frame number h/w is processing */
|
|
+ char *rbuf, *wbuf; /* bounce buffers for conversions */
|
|
+ unsigned long long rpos, wpos; /* bytes read/written */
|
|
+ pthread_t thread; /* thread of the callback interface */
|
|
+} PaSndioStream;
|
|
+
|
|
+/*
|
|
+ * api "class" data, common to all streams
|
|
+ */
|
|
+typedef struct PaSndioHostApiRepresentation
|
|
+{
|
|
+ PaUtilHostApiRepresentation base;
|
|
+ PaUtilStreamInterface callback;
|
|
+ PaUtilStreamInterface blocking;
|
|
+ /*
|
|
+ * sndio has no device discovery mechanism and PortAudio has
|
|
+ * no way of accepting raw device strings from users.
|
|
+ * Normally we just expose the default device, which can be
|
|
+ * changed via the AUDIODEVICE environment variable, but we
|
|
+ * also allow specifying a list of up to 16 devices via the
|
|
+ * PA_SNDIO_AUDIODEVICES environment variable.
|
|
+ *
|
|
+ * Example:
|
|
+ * PA_SNDIO_AUDIODEVICES=default:snd/0.monitor:snd@remote/0
|
|
+ */
|
|
+#define PA_SNDIO_AUDIODEVICES_MAX 16
|
|
+ PaDeviceInfo device_info[PA_SNDIO_AUDIODEVICES_MAX];
|
|
+ PaDeviceInfo *infos[PA_SNDIO_AUDIODEVICES_MAX];
|
|
+ char *audiodevices;
|
|
+} PaSndioHostApiRepresentation;
|
|
+
|
|
+/*
|
|
+ * callback invoked when blocks are processed by the hardware
|
|
+ */
|
|
+static void
|
|
+sndioOnMove(void *addr, int delta)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)addr;
|
|
+
|
|
+ s->realpos += delta;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * convert PA encoding to sndio encoding, return true on success
|
|
+ */
|
|
+static int
|
|
+sndioSetFmt(struct sio_par *sio, PaSampleFormat fmt)
|
|
+{
|
|
+ switch (fmt & ~paNonInterleaved) {
|
|
+ case paInt32:
|
|
+ sio->sig = 1;
|
|
+ sio->bits = 32;
|
|
+ break;
|
|
+ case paInt24:
|
|
+ sio->sig = 1;
|
|
+ sio->bits = 24;
|
|
+ sio->bps = 3; /* paInt24 is packed format */
|
|
+ break;
|
|
+ case paInt16:
|
|
+ case paFloat32:
|
|
+ sio->sig = 1;
|
|
+ sio->bits = 16;
|
|
+ break;
|
|
+ case paInt8:
|
|
+ sio->sig = 1;
|
|
+ sio->bits = 8;
|
|
+ break;
|
|
+ case paUInt8:
|
|
+ sio->sig = 0;
|
|
+ sio->bits = 8;
|
|
+ break;
|
|
+ default:
|
|
+ DPR("sndioSetFmt: %x: unsupported\n", fmt);
|
|
+ return 0;
|
|
+ }
|
|
+ sio->le = SIO_LE_NATIVE;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * convert sndio encoding to PA encoding, return true on success
|
|
+ */
|
|
+static int
|
|
+sndioGetFmt(struct sio_par *sio, PaSampleFormat *fmt)
|
|
+{
|
|
+ if ((sio->bps * 8 != sio->bits && !sio->msb) ||
|
|
+ (sio->bps > 1 && sio->le != SIO_LE_NATIVE)) {
|
|
+ DPR("sndioGetFmt: bits = %u, le = %u, msb = %u, bps = %u\n",
|
|
+ sio->bits, sio->le, sio->msb, sio->bps);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ switch (sio->bits) {
|
|
+ case 32:
|
|
+ if (!sio->sig)
|
|
+ return 0;
|
|
+ *fmt = paInt32;
|
|
+ break;
|
|
+ case 24:
|
|
+ if (!sio->sig)
|
|
+ return 0;
|
|
+ *fmt = (sio->bps == 3) ? paInt24 : paInt32;
|
|
+ break;
|
|
+ case 16:
|
|
+ if (!sio->sig)
|
|
+ return 0;
|
|
+ *fmt = paInt16;
|
|
+ break;
|
|
+ case 8:
|
|
+ *fmt = sio->sig ? paInt8 : paUInt8;
|
|
+ break;
|
|
+ default:
|
|
+ DPR("sndioGetFmt: %u: unsupported\n", sio->bits);
|
|
+ return 0;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * I/O loop for callback interface
|
|
+ */
|
|
+static void *
|
|
+sndioThread(void *arg)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)arg;
|
|
+ PaStreamCallbackTimeInfo ti;
|
|
+ unsigned char *data;
|
|
+ unsigned todo, rblksz, wblksz;
|
|
+ int n, result;
|
|
+
|
|
+ rblksz = s->par.round * s->par.rchan * s->par.bps;
|
|
+ wblksz = s->par.round * s->par.pchan * s->par.bps;
|
|
+
|
|
+ DPR("sndioThread: mode = %x, round = %u, rblksz = %u, wblksz = %u\n",
|
|
+ s->mode, s->par.round, rblksz, wblksz);
|
|
+
|
|
+ while (!s->stopped) {
|
|
+ if (s->mode & SIO_REC) {
|
|
+ todo = rblksz;
|
|
+ data = s->rbuf;
|
|
+ while (todo > 0) {
|
|
+ n = sio_read(s->hdl, data, todo);
|
|
+ if (n == 0) {
|
|
+ DPR("sndioThread: sio_read failed\n");
|
|
+ goto failed;
|
|
+ }
|
|
+ todo -= n;
|
|
+ data += n;
|
|
+ }
|
|
+ s->rpos += s->par.round;
|
|
+ ti.inputBufferAdcTime =
|
|
+ (double)s->realpos / s->par.rate;
|
|
+ }
|
|
+ if (s->mode & SIO_PLAY) {
|
|
+ ti.outputBufferDacTime =
|
|
+ (double)(s->realpos + s->par.bufsz) / s->par.rate;
|
|
+ }
|
|
+ ti.currentTime = s->realpos / (double)s->par.rate;
|
|
+ PaUtil_BeginBufferProcessing(&s->bufproc, &ti, 0);
|
|
+ if (s->mode & SIO_PLAY) {
|
|
+ PaUtil_SetOutputFrameCount(&s->bufproc, s->par.round);
|
|
+ PaUtil_SetInterleavedOutputChannels(&s->bufproc,
|
|
+ 0, s->wbuf, s->par.pchan);
|
|
+ }
|
|
+ if (s->mode & SIO_REC) {
|
|
+ PaUtil_SetInputFrameCount(&s->bufproc, s->par.round);
|
|
+ PaUtil_SetInterleavedInputChannels(&s->bufproc,
|
|
+ 0, s->rbuf, s->par.rchan);
|
|
+ }
|
|
+ result = paContinue;
|
|
+ n = PaUtil_EndBufferProcessing(&s->bufproc, &result);
|
|
+ if (n != s->par.round) {
|
|
+ DPR("sndioThread: %d < %u frames, result = %d\n",
|
|
+ n, s->par.round, result);
|
|
+ }
|
|
+ if (result != paContinue) {
|
|
+ break;
|
|
+ }
|
|
+ if (s->mode & SIO_PLAY) {
|
|
+ n = sio_write(s->hdl, s->wbuf, wblksz);
|
|
+ if (n < wblksz) {
|
|
+ DPR("sndioThread: sio_write failed\n");
|
|
+ goto failed;
|
|
+ }
|
|
+ s->wpos += s->par.round;
|
|
+ }
|
|
+ }
|
|
+ failed:
|
|
+ s->active = 0;
|
|
+ DPR("sndioThread: done\n");
|
|
+}
|
|
+
|
|
+static PaError
|
|
+OpenStream(struct PaUtilHostApiRepresentation *hostApi,
|
|
+ PaStream **stream,
|
|
+ const PaStreamParameters *inputPar,
|
|
+ const PaStreamParameters *outputPar,
|
|
+ double sampleRate,
|
|
+ unsigned long framesPerBuffer,
|
|
+ PaStreamFlags streamFlags,
|
|
+ PaStreamCallback *streamCallback,
|
|
+ void *userData)
|
|
+{
|
|
+ PaSndioHostApiRepresentation *sndioHostApi = (PaSndioHostApiRepresentation *)hostApi;
|
|
+ PaSndioStream *s;
|
|
+ PaError err;
|
|
+ struct sio_hdl *hdl;
|
|
+ struct sio_par par;
|
|
+ unsigned mode;
|
|
+ int inch, onch;
|
|
+ PaSampleFormat ifmt, ofmt, siofmt;
|
|
+ const char *dev;
|
|
+
|
|
+ DPR("OpenStream:\n");
|
|
+
|
|
+ mode = 0;
|
|
+ inch = onch = 0;
|
|
+ ifmt = ofmt = 0;
|
|
+ sio_initpar(&par);
|
|
+
|
|
+ if (outputPar && outputPar->channelCount > 0) {
|
|
+ if (outputPar->device >= sndioHostApi->base.info.deviceCount) {
|
|
+ DPR("OpenStream: %d: bad output device\n", outputPar->device);
|
|
+ return paInvalidDevice;
|
|
+ }
|
|
+ if (outputPar->hostApiSpecificStreamInfo) {
|
|
+ DPR("OpenStream: output specific info\n");
|
|
+ return paIncompatibleHostApiSpecificStreamInfo;
|
|
+ }
|
|
+ if (!sndioSetFmt(&par, outputPar->sampleFormat)) {
|
|
+ return paSampleFormatNotSupported;
|
|
+ }
|
|
+ ofmt = outputPar->sampleFormat;
|
|
+ onch = par.pchan = outputPar->channelCount;
|
|
+ mode |= SIO_PLAY;
|
|
+ }
|
|
+ if (inputPar && inputPar->channelCount > 0) {
|
|
+ if (inputPar->device >= sndioHostApi->base.info.deviceCount) {
|
|
+ DPR("OpenStream: %d: bad input device\n", inputPar->device);
|
|
+ return paInvalidDevice;
|
|
+ }
|
|
+ if (inputPar->hostApiSpecificStreamInfo) {
|
|
+ DPR("OpenStream: input specific info\n");
|
|
+ return paIncompatibleHostApiSpecificStreamInfo;
|
|
+ }
|
|
+ if (!sndioSetFmt(&par, inputPar->sampleFormat)) {
|
|
+ return paSampleFormatNotSupported;
|
|
+ }
|
|
+ ifmt = inputPar->sampleFormat;
|
|
+ inch = par.rchan = inputPar->channelCount;
|
|
+ mode |= SIO_REC;
|
|
+ }
|
|
+ par.rate = sampleRate;
|
|
+ if (framesPerBuffer != paFramesPerBufferUnspecified)
|
|
+ par.round = framesPerBuffer;
|
|
+
|
|
+ DPR("OpenStream: mode = %x, trying rate = %u\n", mode, par.rate);
|
|
+
|
|
+ if (outputPar) {
|
|
+ dev = sndioHostApi->device_info[outputPar->device].name;
|
|
+ } else if (inputPar) {
|
|
+ dev = sndioHostApi->device_info[inputPar->device].name;
|
|
+ } else {
|
|
+ return paUnanticipatedHostError;
|
|
+ }
|
|
+ hdl = sio_open(dev, mode, 0);
|
|
+ if (hdl == NULL)
|
|
+ return paUnanticipatedHostError;
|
|
+ if (!sio_setpar(hdl, &par)) {
|
|
+ sio_close(hdl);
|
|
+ return paUnanticipatedHostError;
|
|
+ }
|
|
+ if (!sio_getpar(hdl, &par)) {
|
|
+ sio_close(hdl);
|
|
+ return paUnanticipatedHostError;
|
|
+ }
|
|
+ if (!sndioGetFmt(&par, &siofmt)) {
|
|
+ sio_close(hdl);
|
|
+ return paSampleFormatNotSupported;
|
|
+ }
|
|
+ if ((mode & SIO_REC) && par.rchan != inputPar->channelCount) {
|
|
+ DPR("OpenStream: rchan(%u) != %d\n", par.rchan, inputPar->channelCount);
|
|
+ sio_close(hdl);
|
|
+ return paInvalidChannelCount;
|
|
+ }
|
|
+ if ((mode & SIO_PLAY) && par.pchan != outputPar->channelCount) {
|
|
+ DPR("OpenStream: pchan(%u) != %d\n", par.pchan, outputPar->channelCount);
|
|
+ sio_close(hdl);
|
|
+ return paInvalidChannelCount;
|
|
+ }
|
|
+ if ((double)par.rate < sampleRate * 0.995 ||
|
|
+ (double)par.rate > sampleRate * 1.005) {
|
|
+ DPR("OpenStream: rate(%u) != %g\n", par.rate, sampleRate);
|
|
+ sio_close(hdl);
|
|
+ return paInvalidSampleRate;
|
|
+ }
|
|
+
|
|
+ s = (PaSndioStream *)PaUtil_AllocateMemory(sizeof(PaSndioStream));
|
|
+ if (s == NULL) {
|
|
+ sio_close(hdl);
|
|
+ return paInsufficientMemory;
|
|
+ }
|
|
+ PaUtil_InitializeStreamRepresentation(&s->base,
|
|
+ streamCallback ? &sndioHostApi->callback : &sndioHostApi->blocking,
|
|
+ streamCallback, userData);
|
|
+ DPR("inch = %d, onch = %d, ifmt = %x, ofmt = %x\n",
|
|
+ inch, onch, ifmt, ofmt);
|
|
+ err = PaUtil_InitializeBufferProcessor(&s->bufproc,
|
|
+ inch, ifmt, siofmt,
|
|
+ onch, ofmt, siofmt,
|
|
+ sampleRate,
|
|
+ streamFlags,
|
|
+ framesPerBuffer,
|
|
+ par.round,
|
|
+ paUtilFixedHostBufferSize,
|
|
+ streamCallback, userData);
|
|
+ if (err) {
|
|
+ DPR("OpenStream: PaUtil_InitializeBufferProcessor failed\n");
|
|
+ PaUtil_FreeMemory(s);
|
|
+ sio_close(hdl);
|
|
+ return err;
|
|
+ }
|
|
+ if (mode & SIO_REC) {
|
|
+ s->rbuf = malloc(par.round * par.rchan * par.bps);
|
|
+ if (s->rbuf == NULL) {
|
|
+ DPR("OpenStream: failed to allocate rbuf\n");
|
|
+ PaUtil_FreeMemory(s);
|
|
+ sio_close(hdl);
|
|
+ return paInsufficientMemory;
|
|
+ }
|
|
+ }
|
|
+ if (mode & SIO_PLAY) {
|
|
+ s->wbuf = malloc(par.round * par.pchan * par.bps);
|
|
+ if (s->wbuf == NULL) {
|
|
+ DPR("OpenStream: failed to allocate wbuf\n");
|
|
+ free(s->rbuf);
|
|
+ PaUtil_FreeMemory(s);
|
|
+ sio_close(hdl);
|
|
+ return paInsufficientMemory;
|
|
+ }
|
|
+ }
|
|
+ s->base.streamInfo.inputLatency = 0;
|
|
+ s->base.streamInfo.outputLatency = (mode & SIO_PLAY) ?
|
|
+ (double)(par.bufsz + PaUtil_GetBufferProcessorOutputLatencyFrames(&s->bufproc)) / (double)par.rate : 0;
|
|
+ s->base.streamInfo.sampleRate = par.rate;
|
|
+ s->active = 0;
|
|
+ s->stopped = 1;
|
|
+ s->mode = mode;
|
|
+ s->hdl = hdl;
|
|
+ s->par = par;
|
|
+ *stream = s;
|
|
+ DPR("OpenStream: done\n");
|
|
+ return paNoError;
|
|
+}
|
|
+
|
|
+static PaError
|
|
+BlockingReadStream(PaStream *stream, void *data, unsigned long numFrames)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+ unsigned n, res, todo;
|
|
+ void *buf;
|
|
+
|
|
+ while (numFrames > 0) {
|
|
+ n = s->par.round;
|
|
+ if (n > numFrames)
|
|
+ n = numFrames;
|
|
+ buf = s->rbuf;
|
|
+ todo = n * s->par.rchan * s->par.bps;
|
|
+ while (todo > 0) {
|
|
+ res = sio_read(s->hdl, buf, todo);
|
|
+ if (res == 0)
|
|
+ return paUnanticipatedHostError;
|
|
+ buf = (char *)buf + res;
|
|
+ todo -= res;
|
|
+ }
|
|
+ s->rpos += n;
|
|
+ PaUtil_SetInputFrameCount(&s->bufproc, n);
|
|
+ PaUtil_SetInterleavedInputChannels(&s->bufproc, 0, s->rbuf, s->par.rchan);
|
|
+ res = PaUtil_CopyInput(&s->bufproc, &data, n);
|
|
+ if (res != n) {
|
|
+ DPR("BlockingReadStream: copyInput: %u != %u\n");
|
|
+ return paUnanticipatedHostError;
|
|
+ }
|
|
+ numFrames -= n;
|
|
+ }
|
|
+ return paNoError;
|
|
+}
|
|
+
|
|
+static PaError
|
|
+BlockingWriteStream(PaStream* stream, const void *data, unsigned long numFrames)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+ unsigned n, res;
|
|
+
|
|
+ while (numFrames > 0) {
|
|
+ n = s->par.round;
|
|
+ if (n > numFrames)
|
|
+ n = numFrames;
|
|
+ PaUtil_SetOutputFrameCount(&s->bufproc, n);
|
|
+ PaUtil_SetInterleavedOutputChannels(&s->bufproc, 0, s->wbuf, s->par.pchan);
|
|
+ res = PaUtil_CopyOutput(&s->bufproc, &data, n);
|
|
+ if (res != n) {
|
|
+ DPR("BlockingWriteStream: copyOutput: %u != %u\n");
|
|
+ return paUnanticipatedHostError;
|
|
+ }
|
|
+ res = sio_write(s->hdl, s->wbuf, n * s->par.pchan * s->par.bps);
|
|
+ if (res == 0)
|
|
+ return paUnanticipatedHostError;
|
|
+ s->wpos += n;
|
|
+ numFrames -= n;
|
|
+ }
|
|
+ return paNoError;
|
|
+}
|
|
+
|
|
+static signed long
|
|
+BlockingGetStreamReadAvailable(PaStream *stream)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+ struct pollfd pfd;
|
|
+ int n, events;
|
|
+
|
|
+ n = sio_pollfd(s->hdl, &pfd, POLLIN);
|
|
+ while (poll(&pfd, n, 0) < 0) {
|
|
+ if (errno == EINTR)
|
|
+ continue;
|
|
+ perror("poll");
|
|
+ abort();
|
|
+ }
|
|
+ events = sio_revents(s->hdl, &pfd);
|
|
+ if (!(events & POLLIN))
|
|
+ return 0;
|
|
+
|
|
+ return s->realpos - s->rpos;
|
|
+}
|
|
+
|
|
+static signed long
|
|
+BlockingGetStreamWriteAvailable(PaStream *stream)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+ struct pollfd pfd;
|
|
+ int n, events;
|
|
+
|
|
+ n = sio_pollfd(s->hdl, &pfd, POLLOUT);
|
|
+ while (poll(&pfd, n, 0) < 0) {
|
|
+ if (errno == EINTR)
|
|
+ continue;
|
|
+ perror("poll");
|
|
+ abort();
|
|
+ }
|
|
+ events = sio_revents(s->hdl, &pfd);
|
|
+ if (!(events & POLLOUT))
|
|
+ return 0;
|
|
+
|
|
+ return s->par.bufsz - (s->wpos - s->realpos);
|
|
+}
|
|
+
|
|
+static PaError
|
|
+BlockingWaitEmpty( PaStream *stream )
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+
|
|
+ /*
|
|
+ * drain playback buffers; sndio always does it in background
|
|
+ * and there is no way to wait for completion
|
|
+ */
|
|
+ DPR("BlockingWaitEmpty: s=%d, a=%d\n", s->stopped, s->active);
|
|
+
|
|
+ return paNoError;
|
|
+}
|
|
+
|
|
+static PaError
|
|
+StartStream(PaStream *stream)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+ unsigned primes, wblksz;
|
|
+ int err;
|
|
+
|
|
+ DPR("StartStream: s=%d, a=%d\n", s->stopped, s->active);
|
|
+
|
|
+ if (!s->stopped) {
|
|
+ DPR("StartStream: already started\n");
|
|
+ return paNoError;
|
|
+ }
|
|
+ s->stopped = 0;
|
|
+ s->active = 1;
|
|
+ s->realpos = 0;
|
|
+ s->wpos = 0;
|
|
+ s->rpos = 0;
|
|
+ PaUtil_ResetBufferProcessor(&s->bufproc);
|
|
+ if (!sio_start(s->hdl))
|
|
+ return paUnanticipatedHostError;
|
|
+
|
|
+ /*
|
|
+ * send a complete buffer of silence
|
|
+ */
|
|
+ if (s->mode & SIO_PLAY) {
|
|
+ wblksz = s->par.round * s->par.pchan * s->par.bps;
|
|
+ memset(s->wbuf, 0, wblksz);
|
|
+ for (primes = s->par.bufsz / s->par.round; primes > 0; primes--)
|
|
+ s->wpos += sio_write(s->hdl, s->wbuf, wblksz);
|
|
+ }
|
|
+ if (s->base.streamCallback) {
|
|
+ err = pthread_create(&s->thread, NULL, sndioThread, s);
|
|
+ if (err) {
|
|
+ DPR("SndioStartStream: couldn't create thread\n");
|
|
+ return paUnanticipatedHostError;
|
|
+ }
|
|
+ DPR("StartStream: started...\n");
|
|
+ }
|
|
+ return paNoError;
|
|
+}
|
|
+
|
|
+static PaError
|
|
+StopStream(PaStream *stream)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+ void *ret;
|
|
+ int err;
|
|
+
|
|
+ DPR("StopStream: s=%d, a=%d\n", s->stopped, s->active);
|
|
+
|
|
+ if (s->stopped) {
|
|
+ DPR("StartStream: already started\n");
|
|
+ return paNoError;
|
|
+ }
|
|
+ s->stopped = 1;
|
|
+ if (s->base.streamCallback) {
|
|
+ err = pthread_join(s->thread, &ret);
|
|
+ if (err) {
|
|
+ DPR("SndioStop: couldn't join thread\n");
|
|
+ return paUnanticipatedHostError;
|
|
+ }
|
|
+ }
|
|
+ if (!sio_stop(s->hdl))
|
|
+ return paUnanticipatedHostError;
|
|
+ return paNoError;
|
|
+}
|
|
+
|
|
+static PaError
|
|
+CloseStream(PaStream *stream)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+
|
|
+ DPR("CloseStream:\n");
|
|
+
|
|
+ if (!s->stopped)
|
|
+ StopStream(stream);
|
|
+
|
|
+ if (s->mode & SIO_REC)
|
|
+ free(s->rbuf);
|
|
+ if (s->mode & SIO_PLAY)
|
|
+ free(s->wbuf);
|
|
+ sio_close(s->hdl);
|
|
+ PaUtil_TerminateStreamRepresentation(&s->base);
|
|
+ PaUtil_TerminateBufferProcessor(&s->bufproc);
|
|
+ PaUtil_FreeMemory(s);
|
|
+ return paNoError;
|
|
+}
|
|
+
|
|
+static PaError
|
|
+AbortStream(PaStream *stream)
|
|
+{
|
|
+ DPR("AbortStream:\n");
|
|
+
|
|
+ return StopStream(stream);
|
|
+}
|
|
+
|
|
+static PaError
|
|
+IsStreamStopped(PaStream *stream)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+
|
|
+ //DPR("IsStreamStopped: s=%d, a=%d\n", s->stopped, s->active);
|
|
+
|
|
+ return s->stopped;
|
|
+}
|
|
+
|
|
+static PaError
|
|
+IsStreamActive(PaStream *stream)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+
|
|
+ //DPR("IsStreamActive: s=%d, a=%d\n", s->stopped, s->active);
|
|
+
|
|
+ return s->active;
|
|
+}
|
|
+
|
|
+static PaTime
|
|
+GetStreamTime(PaStream *stream)
|
|
+{
|
|
+ PaSndioStream *s = (PaSndioStream *)stream;
|
|
+
|
|
+ return (double)s->realpos / s->base.streamInfo.sampleRate;
|
|
+}
|
|
+
|
|
+static PaError
|
|
+IsFormatSupported(struct PaUtilHostApiRepresentation *hostApi,
|
|
+ const PaStreamParameters *inputPar,
|
|
+ const PaStreamParameters *outputPar,
|
|
+ double sampleRate)
|
|
+{
|
|
+ return paFormatIsSupported;
|
|
+}
|
|
+
|
|
+static void
|
|
+Terminate(struct PaUtilHostApiRepresentation *hostApi)
|
|
+{
|
|
+ PaSndioHostApiRepresentation *sndioHostApi;
|
|
+ sndioHostApi = (PaSndioHostApiRepresentation *)hostApi;
|
|
+ free(sndioHostApi->audiodevices);
|
|
+ PaUtil_FreeMemory(hostApi);
|
|
+}
|
|
+
|
|
+static void
|
|
+InitDeviceInfo(PaDeviceInfo *info, PaHostApiIndex hostApiIndex, const char *name)
|
|
+{
|
|
+ info->structVersion = 2;
|
|
+ info->name = name;
|
|
+ info->hostApi = hostApiIndex;
|
|
+ info->maxInputChannels = 128;
|
|
+ info->maxOutputChannels = 128;
|
|
+ info->defaultLowInputLatency = 0.01;
|
|
+ info->defaultLowOutputLatency = 0.01;
|
|
+ info->defaultHighInputLatency = 0.5;
|
|
+ info->defaultHighOutputLatency = 0.5;
|
|
+ info->defaultSampleRate = 48000;
|
|
+}
|
|
+
|
|
+PaError
|
|
+PaSndio_Initialize(PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex)
|
|
+{
|
|
+ PaSndioHostApiRepresentation *sndioHostApi;
|
|
+ PaDeviceInfo *info;
|
|
+ struct sio_hdl *hdl;
|
|
+ char *audiodevices;
|
|
+ char *device;
|
|
+ size_t deviceCount;
|
|
+
|
|
+ DPR("PaSndio_Initialize: initializing...\n");
|
|
+
|
|
+ /* unusable APIs should return paNoError and a NULL hostApi */
|
|
+ *hostApi = NULL;
|
|
+
|
|
+ sndioHostApi = PaUtil_AllocateMemory(sizeof(PaSndioHostApiRepresentation));
|
|
+ if (sndioHostApi == NULL)
|
|
+ return paNoError;
|
|
+
|
|
+ // Add default device
|
|
+ info = &sndioHostApi->device_info[0];
|
|
+ InitDeviceInfo(info, hostApiIndex, SIO_DEVANY);
|
|
+ sndioHostApi->infos[0] = info;
|
|
+ deviceCount = 1;
|
|
+
|
|
+ // Add additional devices as specified in the PA_SNDIO_AUDIODEVICES
|
|
+ // environment variable as a colon separated list
|
|
+ sndioHostApi->audiodevices = NULL;
|
|
+ audiodevices = getenv("PA_SNDIO_AUDIODEVICES");
|
|
+ if (audiodevices != NULL) {
|
|
+ sndioHostApi->audiodevices = strdup(audiodevices);
|
|
+ if (sndioHostApi->audiodevices == NULL)
|
|
+ return paNoError;
|
|
+
|
|
+ audiodevices = sndioHostApi->audiodevices;
|
|
+ while ((device = strsep(&audiodevices, ":")) != NULL &&
|
|
+ deviceCount < PA_SNDIO_AUDIODEVICES_MAX) {
|
|
+ if (*device == '\0')
|
|
+ continue;
|
|
+ info = &sndioHostApi->device_info[deviceCount];
|
|
+ InitDeviceInfo(info, hostApiIndex, device);
|
|
+ sndioHostApi->infos[deviceCount] = info;
|
|
+ deviceCount++;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *hostApi = &sndioHostApi->base;
|
|
+ (*hostApi)->info.structVersion = 1;
|
|
+ (*hostApi)->info.type = paSndio;
|
|
+ (*hostApi)->info.name = "sndio";
|
|
+ (*hostApi)->info.deviceCount = deviceCount;
|
|
+ (*hostApi)->info.defaultInputDevice = 0;
|
|
+ (*hostApi)->info.defaultOutputDevice = 0;
|
|
+ (*hostApi)->deviceInfos = sndioHostApi->infos;
|
|
+ (*hostApi)->Terminate = Terminate;
|
|
+ (*hostApi)->OpenStream = OpenStream;
|
|
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
|
|
+
|
|
+ PaUtil_InitializeStreamInterface(&sndioHostApi->blocking,
|
|
+ CloseStream,
|
|
+ StartStream,
|
|
+ StopStream,
|
|
+ AbortStream,
|
|
+ IsStreamStopped,
|
|
+ IsStreamActive,
|
|
+ GetStreamTime,
|
|
+ PaUtil_DummyGetCpuLoad,
|
|
+ BlockingReadStream,
|
|
+ BlockingWriteStream,
|
|
+ BlockingGetStreamReadAvailable,
|
|
+ BlockingGetStreamWriteAvailable);
|
|
+
|
|
+ PaUtil_InitializeStreamInterface(&sndioHostApi->callback,
|
|
+ CloseStream,
|
|
+ StartStream,
|
|
+ StopStream,
|
|
+ AbortStream,
|
|
+ IsStreamStopped,
|
|
+ IsStreamActive,
|
|
+ GetStreamTime,
|
|
+ PaUtil_DummyGetCpuLoad,
|
|
+ PaUtil_DummyRead,
|
|
+ PaUtil_DummyWrite,
|
|
+ PaUtil_DummyGetReadAvailable,
|
|
+ PaUtil_DummyGetWriteAvailable);
|
|
+
|
|
+ DPR("PaSndio_Initialize: done\n");
|
|
+ return paNoError;
|
|
+}
|
|
diff --git src/os/unix/pa_unix_hostapis.c src/os/unix/pa_unix_hostapis.c
|
|
index a9b4a05..c3fa2a3 100644
|
|
--- src/os/unix/pa_unix_hostapis.c
|
|
+++ src/os/unix/pa_unix_hostapis.c
|
|
@@ -44,6 +44,7 @@
|
|
|
|
PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
|
PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
|
+PaError PaSndio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
|
PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
|
/* Added for IRIX, Pieter, oct 2, 2003: */
|
|
PaError PaSGI_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
|
|
@@ -79,6 +80,10 @@ PaUtilHostApiInitializer *paHostApiInitializers[] =
|
|
|
|
#endif /* __linux__ */
|
|
|
|
+#ifdef PA_USE_SNDIO
|
|
+ PaSndio_Initialize,
|
|
+#endif
|
|
+
|
|
#if PA_USE_JACK
|
|
PaJack_Initialize,
|
|
#endif
|