Source: portaudio19
Version: 19.6.0-1.2
Severity: wishlist
Tags: patch
X-Debbugs-Cc: jobbautis...@protonmail.com
Dear Maintainer,
I think it would be nice to have sndio support in the PortAudio package. This
allows applications dependent on the library like Audacity to use this sound
server instead of falling back to ALSA (and thus causing conflicts with other
sndio programs). I've attached a patch which should build successfully against
19.6.0, backported from a still-open pull request for 19.7.0.
Thanks
-- System Information:
Debian Release: bookworm/sid
APT prefers unreleased
APT policy: (1, 'unreleased'), (1, 'experimental'), (1, 'unstable')
merged-usr: no
Architecture: amd64 (x86_64)
Foreign Architectures: i386, hurd-i386
Kernel: Linux 5.19.0-1-amd64 (SMP w/4 CPU threads; PREEMPT)
Kernel taint flags: TAINT_WARN, TAINT_FIRMWARE_WORKAROUND, TAINT_OOT_MODULE,
TAINT_UNSIGNED_MODULE
Locale: LANG=en_PH.UTF-8, LC_CTYPE=en_PH.UTF-8 (charmap=UTF-8),
LANGUAGE=en_PH:en
Shell: /bin/sh linked to /bin/dash
Init: OpenRC (via /run/openrc), PID 1: init
LSM: AppArmor: enabled
From: Job Bautista <jobbautis...@protonmail.com>
Subject: Add support for sndio
Attempt to push sndio support to upstream is currently stalled.
I only did the CMakeLists.txt additions. The rest is copied from lanodan's work.
Author: Alexandre Ratchov <a...@caoua.org>
Author: Haelwenn (lanodan) Monnier <cont...@hacktivis.me>
Author: Job Bautista <jobbautis...@protonmail.com>
Forwarded: https://github.com/PortAudio/portaudio/pull/647
Last-Update: 2022-09-21
--- portaudio19-19.6.0.orig/CMakeLists.txt
+++ portaudio19-19.6.0/CMakeLists.txt
@@ -314,6 +314,22 @@ ELSE()
SET(PA_PKGCONFIG_LDFLAGS "${PA_PKGCONFIG_LDFLAGS} -lasound")
ENDIF()
+ FIND_PACKAGE(SNDIO)
+ IF(SNDIO_FOUND)
+ OPTION(PA_USE_SNDIO "Enable support for sndio" ON)
+ ELSE()
+ OPTION(PA_USE_SNDIO "Enable support for sndio" OFF)
+ ENDIF()
+ IF(PA_USE_SNDIO)
+ SET(PA_PRIVATE_INCLUDE_PATHS ${PA_PRIVATE_INCLUDE_PATHS} ${SNDIO_INCLUDE_DIRS})
+ SET(PA_SNDIO_SOURCES src/hostapi/sndio/pa_sndio.c)
+ SOURCE_GROUP("hostapi\\SNDIO" FILES ${PA_SNDIO_SOURCES})
+ SET(PA_SOURCES ${PA_SOURCES} ${PA_SNDIO_SOURCES})
+ SET(PA_PRIVATE_COMPILE_DEFINITIONS ${PA_PRIVATE_COMPILE_DEFINITIONS} PA_USE_SNDIO)
+ SET(PA_LIBRARY_DEPENDENCIES ${PA_LIBRARY_DEPENDENCIES} ${SNDIO_LIBRARIES})
+ SET(PA_PKGCONFIG_LDFLAGS "${PA_PKGCONFIG_LDFLAGS} -lsndio")
+ ENDIF()
+
ENDIF()
SET(PA_PKGCONFIG_LDFLAGS "${PA_PKGCONFIG_LDFLAGS} -lm -lpthread")
--- portaudio19-19.6.0.orig/Makefile.in
+++ portaudio19-19.6.0/Makefile.in
@@ -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 \
--- portaudio19-19.6.0.orig/README.txt
+++ portaudio19-19.6.0/README.txt
@@ -77,6 +77,7 @@ Host API Implementations:
src/hostapi/dsound = Windows Direct Sound
src/hostapi/jack = JACK Audio Connection Kit
src/hostapi/oss = Unix Open Sound System (OSS)
+ src/hostapi/sndio = Small audio and MIDI framework (sndio)
src/hostapi/wasapi = Windows Vista WASAPI
src/hostapi/wdmks = Windows WDM Kernel Streaming
src/hostapi/wmme = Windows MultiMedia Extensions (MME)
--- portaudio19-19.6.0.orig/configure.in
+++ portaudio19-19.6.0/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"
@@ -512,6 +527,7 @@ case "$target_os" in
AC_MSG_RESULT([
OSS ......................... $have_oss
JACK ........................ $have_jack
+ Sndio ....................... $have_sndio
])
;;
esac
--- portaudio19-19.6.0.orig/include/portaudio.h
+++ portaudio19-19.6.0/include/portaudio.h
@@ -287,7 +287,8 @@ typedef enum PaHostApiTypeId
paWDMKS=11,
paJACK=12,
paWASAPI=13,
- paAudioScienceHPI=14
+ paAudioScienceHPI=14,
+ paSndio=16
} PaHostApiTypeId;
--- /dev/null
+++ portaudio19-19.6.0/src/hostapi/sndio/pa_sndio.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (c) 2009 Alexandre Ratchov <a...@caoua.org>
+ * Copyright (c) 2021 Haelwenn (lanodan) Monnier <cont...@hacktivis.me>
+ *
+ * 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.
+ */
+/*
+ * Indentation is done via the .clang-format at the root of the repo
+ * and the following command:
+ * clang-format -style=file -assume-filename=.clang-format -i src/hostapi/sndio/pa_sndio.c
+ */
+#include <errno.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sndio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pa_allocation.h"
+#include "pa_debugprint.h"
+#include "pa_hostapi.h"
+#include "pa_process.h"
+#include "pa_stream.h"
+#include "pa_util.h"
+
+/*
+ * 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 *sndioStream = (PaSndioStream *)addr;
+
+ sndioStream->realpos += delta;
+}
+
+/*
+ * convert PA encoding to sndio encoding, return true on success
+ */
+static int sndioSetFmt( struct sio_par *par, PaSampleFormat fmt )
+{
+ switch( fmt & ~paNonInterleaved )
+ {
+ case paInt32:
+ case paFloat32:
+ par->sig = 1;
+ par->bits = 32;
+ break;
+ case paInt24:
+ par->sig = 1;
+ par->bits = 24;
+ par->bps = 3; /* paInt24 is packed format */
+ break;
+ case paInt16:
+ par->sig = 1;
+ par->bits = 16;
+ break;
+ case paInt8:
+ par->sig = 1;
+ par->bits = 8;
+ break;
+ case paUInt8:
+ par->sig = 0;
+ par->bits = 8;
+ break;
+ default:
+ PA_DEBUG( ( "sndioSetFmt: %x: unsupported\n", fmt ) );
+ return 0;
+ }
+ par->le = SIO_LE_NATIVE;
+ return 1;
+}
+
+/*
+ * convert sndio encoding to PA encoding, return true on success
+ */
+static int sndioGetFmt( struct sio_par *par, PaSampleFormat *fmt )
+{
+ if( ( par->bps * 8 != par->bits && !par->msb ) || ( par->bps > 1 && par->le != SIO_LE_NATIVE ) )
+ {
+ PA_DEBUG( ( "sndioGetFmt: bits = %u, le = %u, msb = %u, bps = %u\n", par->bits, par->le, par->msb, par->bps ) );
+ return 0;
+ }
+
+ switch( par->bits )
+ {
+ case 32:
+ if( !par->sig )
+ return 0;
+ *fmt = paInt32;
+ break;
+ case 24:
+ if( !par->sig )
+ return 0;
+ *fmt = ( par->bps == 3 ) ? paInt24 : paInt32;
+ break;
+ case 16:
+ if( !par->sig )
+ return 0;
+ *fmt = paInt16;
+ break;
+ case 8:
+ *fmt = par->sig ? paInt8 : paUInt8;
+ break;
+ default:
+ PA_DEBUG( ( "sndioGetFmt: %u: unsupported\n", par->bits ) );
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * I/O loop for callback interface
+ */
+static void *sndioThread( void *arg )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)arg;
+ PaStreamCallbackTimeInfo timeinfo;
+ unsigned char *data;
+ unsigned todo, rblksz, wblksz;
+ int n, result;
+
+ rblksz = sndioStream->par.round * sndioStream->par.rchan * sndioStream->par.bps;
+ wblksz = sndioStream->par.round * sndioStream->par.pchan * sndioStream->par.bps;
+
+ PA_DEBUG( ( "sndioThread: mode = %x, round = %u, rblksz = %u, wblksz = %u\n", sndioStream->mode,
+ sndioStream->par.round, rblksz, wblksz ) );
+
+ while( !sndioStream->stopped )
+ {
+ if( sndioStream->mode & SIO_REC )
+ {
+ todo = rblksz;
+ data = sndioStream->rbuf;
+ while( todo > 0 )
+ {
+ n = sio_read( sndioStream->hdl, data, todo );
+ if( n == 0 )
+ {
+ PA_DEBUG( ( "sndioThread: sio_read failed\n" ) );
+ goto failed;
+ }
+ todo -= n;
+ data += n;
+ }
+ sndioStream->rpos += sndioStream->par.round;
+ timeinfo.inputBufferAdcTime = (double)sndioStream->realpos / sndioStream->par.rate;
+ }
+ if( sndioStream->mode & SIO_PLAY )
+ {
+ timeinfo.outputBufferDacTime =
+ (double)( sndioStream->realpos + sndioStream->par.bufsz ) / sndioStream->par.rate;
+ }
+ timeinfo.currentTime = sndioStream->realpos / (double)sndioStream->par.rate;
+ PaUtil_BeginBufferProcessing( &sndioStream->bufproc, &timeinfo, 0 );
+ if( sndioStream->mode & SIO_PLAY )
+ {
+ PaUtil_SetOutputFrameCount( &sndioStream->bufproc, sndioStream->par.round );
+ PaUtil_SetInterleavedOutputChannels( &sndioStream->bufproc, 0, sndioStream->wbuf,
+ sndioStream->par.pchan );
+ }
+ if( sndioStream->mode & SIO_REC )
+ {
+ PaUtil_SetInputFrameCount( &sndioStream->bufproc, sndioStream->par.round );
+ PaUtil_SetInterleavedInputChannels( &sndioStream->bufproc, 0, sndioStream->rbuf,
+ sndioStream->par.rchan );
+ }
+ result = paContinue;
+ n = PaUtil_EndBufferProcessing( &sndioStream->bufproc, &result );
+ if( n != sndioStream->par.round )
+ {
+ PA_DEBUG( ( "sndioThread: %d < %u frames, result = %d\n", n, sndioStream->par.round, result ) );
+ }
+ if( result != paContinue )
+ {
+ break;
+ }
+ if( sndioStream->mode & SIO_PLAY )
+ {
+ n = sio_write( sndioStream->hdl, sndioStream->wbuf, wblksz );
+ if( n < wblksz )
+ {
+ PA_DEBUG( ( "sndioThread: sio_write failed\n" ) );
+ goto failed;
+ }
+ sndioStream->wpos += sndioStream->par.round;
+ }
+ }
+failed:
+ sndioStream->active = 0;
+ PA_DEBUG( ( "sndioThread: done\n" ) );
+ return NULL;
+}
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaStream **paStream,
+ const PaStreamParameters *inputPar, const PaStreamParameters *outputPar, double sampleRate,
+ unsigned long framesPerBuffer, PaStreamFlags streamFlags, PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaSndioHostApiRepresentation *sndioHostApi = (PaSndioHostApiRepresentation *)hostApi;
+ PaSndioStream *sndioStream;
+ PaError err;
+ struct sio_hdl *hdl;
+ struct sio_par par;
+ unsigned mode;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat ifmt, ofmt, siofmt;
+ const char *dev;
+
+ PA_DEBUG( ( "OpenStream:\n" ) );
+
+ mode = 0;
+ inputChannelCount = outputChannelCount = 0;
+ ifmt = ofmt = 0;
+ sio_initpar( &par );
+
+ if( outputPar && outputPar->channelCount > 0 )
+ {
+ if( outputPar->device >= sndioHostApi->base.info.deviceCount )
+ {
+ PA_DEBUG( ( "OpenStream: %d: bad output device\n", outputPar->device ) );
+ return paInvalidDevice;
+ }
+ if( outputPar->hostApiSpecificStreamInfo )
+ {
+ PA_DEBUG( ( "OpenStream: output specific info\n" ) );
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ if( !sndioSetFmt( &par, outputPar->sampleFormat ) )
+ {
+ return paSampleFormatNotSupported;
+ }
+ ofmt = outputPar->sampleFormat;
+ outputChannelCount = par.pchan = outputPar->channelCount;
+ mode |= SIO_PLAY;
+ }
+ if( inputPar && inputPar->channelCount > 0 )
+ {
+ if( inputPar->device >= sndioHostApi->base.info.deviceCount )
+ {
+ PA_DEBUG( ( "OpenStream: %d: bad input device\n", inputPar->device ) );
+ return paInvalidDevice;
+ }
+ if( inputPar->hostApiSpecificStreamInfo )
+ {
+ PA_DEBUG( ( "OpenStream: input specific info\n" ) );
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ if( !sndioSetFmt( &par, inputPar->sampleFormat ) )
+ {
+ return paSampleFormatNotSupported;
+ }
+ ifmt = inputPar->sampleFormat;
+ inputChannelCount = par.rchan = inputPar->channelCount;
+ mode |= SIO_REC;
+ }
+ par.rate = sampleRate;
+ if( framesPerBuffer != paFramesPerBufferUnspecified )
+ par.round = framesPerBuffer;
+
+ PA_DEBUG( ( "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 )
+ {
+ PA_DEBUG( ( "OpenStream: rchan(%u) != %d\n", par.rchan, inputPar->channelCount ) );
+ sio_close( hdl );
+ return paInvalidChannelCount;
+ }
+ if( ( mode & SIO_PLAY ) && par.pchan != outputPar->channelCount )
+ {
+ PA_DEBUG( ( "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 )
+ {
+ PA_DEBUG( ( "OpenStream: rate(%u) != %g\n", par.rate, sampleRate ) );
+ sio_close( hdl );
+ return paInvalidSampleRate;
+ }
+
+ sndioStream = (PaSndioStream *)PaUtil_AllocateMemory( sizeof( PaSndioStream ) );
+ if( sndioStream == NULL )
+ {
+ sio_close( hdl );
+ return paInsufficientMemory;
+ }
+ PaUtil_InitializeStreamRepresentation( &sndioStream->base,
+ streamCallback ? &sndioHostApi->callback : &sndioHostApi->blocking,
+ streamCallback, userData );
+ PA_DEBUG( ( "inputChannelCount = %d, outputChannelCount = %d, ifmt = %x, ofmt = %x\n", inputChannelCount,
+ outputChannelCount, ifmt, ofmt ) );
+ err = PaUtil_InitializeBufferProcessor( &sndioStream->bufproc, inputChannelCount, ifmt, siofmt, outputChannelCount,
+ ofmt, siofmt, sampleRate, streamFlags, framesPerBuffer, par.round,
+ paUtilFixedHostBufferSize, streamCallback, userData );
+ if( err )
+ {
+ PA_DEBUG( ( "OpenStream: PaUtil_InitializeBufferProcessor failed\n" ) );
+ PaUtil_FreeMemory( sndioStream );
+ sio_close( hdl );
+ return err;
+ }
+ if( mode & SIO_REC )
+ {
+ sndioStream->rbuf = malloc( par.round * par.rchan * par.bps );
+ if( sndioStream->rbuf == NULL )
+ {
+ PA_DEBUG( ( "OpenStream: failed to allocate rbuf\n" ) );
+ PaUtil_FreeMemory( sndioStream );
+ sio_close( hdl );
+ return paInsufficientMemory;
+ }
+ }
+ if( mode & SIO_PLAY )
+ {
+ sndioStream->wbuf = malloc( par.round * par.pchan * par.bps );
+ if( sndioStream->wbuf == NULL )
+ {
+ PA_DEBUG( ( "OpenStream: failed to allocate wbuf\n" ) );
+ free( sndioStream->rbuf );
+ PaUtil_FreeMemory( sndioStream );
+ sio_close( hdl );
+ return paInsufficientMemory;
+ }
+ }
+ sndioStream->base.streamInfo.inputLatency = 0;
+ sndioStream->base.streamInfo.outputLatency =
+ ( mode & SIO_PLAY )
+ ? (double)( par.bufsz + PaUtil_GetBufferProcessorOutputLatencyFrames( &sndioStream->bufproc ) ) /
+ (double)par.rate
+ : 0;
+ sndioStream->base.streamInfo.sampleRate = par.rate;
+ sndioStream->active = 0;
+ sndioStream->stopped = 1;
+ sndioStream->mode = mode;
+ sndioStream->hdl = hdl;
+ sndioStream->par = par;
+ *paStream = sndioStream;
+ PA_DEBUG( ( "OpenStream: done\n" ) );
+ return paNoError;
+}
+
+static PaError BlockingReadStream( PaStream *paStream, void *data, unsigned long numFrames )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+ unsigned n, res, todo;
+ void *buf;
+
+ while( numFrames > 0 )
+ {
+ n = sndioStream->par.round;
+ if( n > numFrames )
+ n = numFrames;
+ buf = sndioStream->rbuf;
+ todo = n * sndioStream->par.rchan * sndioStream->par.bps;
+ while( todo > 0 )
+ {
+ res = sio_read( sndioStream->hdl, buf, todo );
+ if( res == 0 )
+ return paUnanticipatedHostError;
+ buf = (char *)buf + res;
+ todo -= res;
+ }
+ sndioStream->rpos += n;
+ PaUtil_SetInputFrameCount( &sndioStream->bufproc, n );
+ PaUtil_SetInterleavedInputChannels( &sndioStream->bufproc, 0, sndioStream->rbuf, sndioStream->par.rchan );
+ res = PaUtil_CopyInput( &sndioStream->bufproc, &data, n );
+ if( res != n )
+ {
+ PA_DEBUG( ( "BlockingReadStream: copyInput: %u != %u\n" ) );
+ return paUnanticipatedHostError;
+ }
+ numFrames -= n;
+ }
+ return paNoError;
+}
+
+static PaError BlockingWriteStream( PaStream *paStream, const void *data, unsigned long numFrames )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+ unsigned n, res;
+
+ while( numFrames > 0 )
+ {
+ n = sndioStream->par.round;
+ if( n > numFrames )
+ n = numFrames;
+ PaUtil_SetOutputFrameCount( &sndioStream->bufproc, n );
+ PaUtil_SetInterleavedOutputChannels( &sndioStream->bufproc, 0, sndioStream->wbuf, sndioStream->par.pchan );
+ res = PaUtil_CopyOutput( &sndioStream->bufproc, &data, n );
+ if( res != n )
+ {
+ PA_DEBUG( ( "BlockingWriteStream: copyOutput: %u != %u\n" ) );
+ return paUnanticipatedHostError;
+ }
+ res = sio_write( sndioStream->hdl, sndioStream->wbuf, n * sndioStream->par.pchan * sndioStream->par.bps );
+ if( res == 0 )
+ return paUnanticipatedHostError;
+ sndioStream->wpos += n;
+ numFrames -= n;
+ }
+ return paNoError;
+}
+
+static signed long BlockingGetStreamReadAvailable( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+ struct pollfd pfd;
+ int n, events;
+
+ n = sio_pollfd( sndioStream->hdl, &pfd, POLLIN );
+ while( poll( &pfd, n, 0 ) < 0 )
+ {
+ if( errno == EINTR )
+ continue;
+ perror( "poll" );
+ abort();
+ }
+ events = sio_revents( sndioStream->hdl, &pfd );
+ if( !( events & POLLIN ) )
+ return 0;
+
+ return sndioStream->realpos - sndioStream->rpos;
+}
+
+static signed long BlockingGetStreamWriteAvailable( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+ struct pollfd pfd;
+ int n, events;
+
+ n = sio_pollfd( sndioStream->hdl, &pfd, POLLOUT );
+ while( poll( &pfd, n, 0 ) < 0 )
+ {
+ if( errno == EINTR )
+ continue;
+ perror( "poll" );
+ abort();
+ }
+ events = sio_revents( sndioStream->hdl, &pfd );
+ if( !( events & POLLOUT ) )
+ return 0;
+
+ return sndioStream->par.bufsz - ( sndioStream->wpos - sndioStream->realpos );
+}
+
+static PaError BlockingWaitEmpty( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+
+ /*
+ * drain playback buffers; sndio always does it in background
+ * and there is no way to wait for completion
+ */
+ PA_DEBUG( ( "BlockingWaitEmpty: s=%d, a=%d\n", sndioStream->stopped, sndioStream->active ) );
+
+ return paNoError;
+}
+
+static PaError StartStream( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+ unsigned primes, wblksz;
+ int err;
+
+ PA_DEBUG( ( "StartStream: s=%d, a=%d\n", sndioStream->stopped, sndioStream->active ) );
+
+ if( !sndioStream->stopped )
+ {
+ PA_DEBUG( ( "StartStream: already started\n" ) );
+ return paNoError;
+ }
+ sndioStream->stopped = 0;
+ sndioStream->active = 1;
+ sndioStream->realpos = 0;
+ sndioStream->wpos = 0;
+ sndioStream->rpos = 0;
+ PaUtil_ResetBufferProcessor( &sndioStream->bufproc );
+ if( !sio_start( sndioStream->hdl ) )
+ return paUnanticipatedHostError;
+
+ /*
+ * send a complete buffer of silence
+ */
+ if( sndioStream->mode & SIO_PLAY )
+ {
+ wblksz = sndioStream->par.round * sndioStream->par.pchan * sndioStream->par.bps;
+ memset( sndioStream->wbuf, 0, wblksz );
+ for( primes = sndioStream->par.bufsz / sndioStream->par.round; primes > 0; primes-- )
+ sndioStream->wpos += sio_write( sndioStream->hdl, sndioStream->wbuf, wblksz );
+ }
+ if( sndioStream->base.streamCallback )
+ {
+ err = pthread_create( &sndioStream->thread, NULL, sndioThread, sndioStream );
+ if( err )
+ {
+ PA_DEBUG( ( "SndioStartStream: couldn't create thread\n" ) );
+ return paUnanticipatedHostError;
+ }
+ PA_DEBUG( ( "StartStream: started...\n" ) );
+ }
+ return paNoError;
+}
+
+static PaError StopStream( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+ void *ret;
+ int err;
+
+ PA_DEBUG( ( "StopStream: s=%d, a=%d\n", sndioStream->stopped, sndioStream->active ) );
+
+ if( sndioStream->stopped )
+ {
+ PA_DEBUG( ( "StartStream: already started\n" ) );
+ return paNoError;
+ }
+ sndioStream->stopped = 1;
+ if( sndioStream->base.streamCallback )
+ {
+ err = pthread_join( sndioStream->thread, &ret );
+ if( err )
+ {
+ PA_DEBUG( ( "SndioStop: couldn't join thread\n" ) );
+ return paUnanticipatedHostError;
+ }
+ }
+ if( !sio_stop( sndioStream->hdl ) )
+ return paUnanticipatedHostError;
+ return paNoError;
+}
+
+static PaError CloseStream( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+
+ PA_DEBUG( ( "CloseStream:\n" ) );
+
+ if( !sndioStream->stopped )
+ StopStream( paStream );
+
+ if( sndioStream->mode & SIO_REC )
+ free( sndioStream->rbuf );
+ if( sndioStream->mode & SIO_PLAY )
+ free( sndioStream->wbuf );
+ sio_close( sndioStream->hdl );
+ PaUtil_TerminateStreamRepresentation( &sndioStream->base );
+ PaUtil_TerminateBufferProcessor( &sndioStream->bufproc );
+ PaUtil_FreeMemory( sndioStream );
+ return paNoError;
+}
+
+static PaError AbortStream( PaStream *paStream )
+{
+ PA_DEBUG( ( "AbortStream:\n" ) );
+
+ return StopStream( paStream );
+}
+
+static PaError IsStreamStopped( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+
+ // PA_DEBUG(("IsStreamStopped: s=%d, a=%d\n", sndioStream->stopped, sndioStream->active));
+
+ return sndioStream->stopped;
+}
+
+static PaError IsStreamActive( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+
+ // PA_DEBUG(("IsStreamActive: s=%d, a=%d\n", sndioStream->stopped, sndioStream->active));
+
+ return sndioStream->active;
+}
+
+static PaTime GetStreamTime( PaStream *paStream )
+{
+ PaSndioStream *sndioStream = (PaSndioStream *)paStream;
+
+ return (double)sndioStream->realpos / sndioStream->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;
+
+ PA_DEBUG( ( "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 );
+
+ PA_DEBUG( ( "PaSndio_Initialize: done\n" ) );
+ return paNoError;
+}
--- portaudio19-19.6.0.orig/src/os/unix/pa_unix_hostapis.c
+++ portaudio19-19.6.0/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 );
@@ -57,6 +58,10 @@ PaError PaSkeleton_Initialize( PaUtilHos
PaUtilHostApiInitializer *paHostApiInitializers[] =
{
+#ifdef PA_USE_SNDIO
+ PaSndio_Initialize,
+#endif
+
#ifdef __linux__
#if PA_USE_ALSA