commit: f606936a4991b84f8fdfd8546dd03b6fa4d4e7b9
parent: 5170336ca453b8bb3fb18a245f124bb62d736a19
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Wed, 27 May 2020 01:00:00 +0200
media-libs/portaudio: New (sndio)
Diffstat:
4 files changed, 974 insertions(+), 0 deletions(-)
diff --git a/media-libs/portaudio/Manifest b/media-libs/portaudio/Manifest
@@ -0,0 +1,2 @@
+DIST pa_stable_v190600_20161030.tgz 1450572 BLAKE2B 3cd63cca55ff222f452d306a1ab9035739545f48dbc9bc58196052cb51e518d86568059c7528533a6932c39c68d1a5210e06470a16343b92c7e4132b71cc552e SHA512 7ec692cbd8c23878b029fad9d9fd63a021f57e60c4921f602995a2fca070c29f17a280c7f2da5966c4aad29d28434538452f4c822eacf3a60af59a6dc8e9704c
+DIST portaudio-19.06.00-audacity-portmixer.patch 8722 BLAKE2B 2b69bcf26e55be46d354dc44756fd03b16031fa86a334b776eb4326a4c64a7e22abd6bf60794847526102391c8e65813b41338cf94c647e3e9b68d82ea5dc5f5 SHA512 f693aef477b516c7a03eb021260cbecb23200422a927a117e3e59a1b4e6c6c2983fbea8ee3a3714ebc55728933654bd7909327a0bab4450ccf4e6a02c86a63b5
diff --git a/media-libs/portaudio/files/portaudio-19.06.00-sndio.patch b/media-libs/portaudio/files/portaudio-19.06.00-sndio.patch
@@ -0,0 +1,881 @@
+Source: https://github.com/void-linux/void-packages/blob/master/srcpkgs/portaudio/patches/sndio.patch
+
+Requires an eautoreconf.
+
+diff --git Makefile.in Makefile.in
+index 5e1a764..2747f73 100644
+--- a/Makefile.in
++++ b/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
+--- a/configure.in
++++ b/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
+--- a/include/portaudio.h
++++ b/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
+--- a//dev/null
++++ b/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
+--- a/src/os/unix/pa_unix_hostapis.c
++++ b/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
diff --git a/media-libs/portaudio/metadata.xml b/media-libs/portaudio/metadata.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
+<pkgmetadata>
+<maintainer type="project">
+ <email>sound@gentoo.org</email>
+ <name>Gentoo Sound project</name>
+</maintainer>
+</pkgmetadata>
diff --git a/media-libs/portaudio/portaudio-19.06.00-r2.ebuild b/media-libs/portaudio/portaudio-19.06.00-r2.ebuild
@@ -0,0 +1,83 @@
+# Copyright 1999-2020 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+EAPI=7
+inherit multilib-minimal autotools
+
+DESCRIPTION="A free, cross-platform, open-source, audio I/O library"
+HOMEPAGE="http://www.portaudio.com/"
+SRC_URI="http://www.portaudio.com/archives/pa_stable_v190600_20161030.tgz
+ https://sources.debian.org/data/main/p/portaudio19/19.6.0-1/debian/patches/audacity-portmixer.patch -> ${PN}-19.06.00-audacity-portmixer.patch"
+
+LICENSE="MIT"
+SLOT="0"
+KEYWORDS="~alpha ~amd64 ~arm ~arm64 ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86 ~amd64-linux ~x86-linux"
+IUSE="alsa +cxx debug doc jack oss static-libs sndio"
+
+RDEPEND="alsa? ( >=media-libs/alsa-lib-1.0.27.2[${MULTILIB_USEDEP}] )
+ jack? ( virtual/jack[${MULTILIB_USEDEP}] )
+ sndio? ( media-sound/sndio:=[${MULTILIB_USEDEP}] )
+"
+DEPEND="${RDEPEND}"
+BDEPEND="
+ doc? ( app-doc/doxygen )
+ virtual/pkgconfig
+"
+
+S="${WORKDIR}/${PN}"
+
+DOCS=( README.txt )
+
+PATCHES=(
+ "${DISTDIR}/${PN}-19.06.00-audacity-portmixer.patch"
+ "${FILESDIR}/${PN}-19.06.00-sndio.patch"
+)
+
+src_prepare() {
+ default
+
+ # depcomp is required when building the bindings/cpp extension
+ # but will be removed by autoreconf + libtool >= 2.4.6
+ # Protect it from removal
+ mv depcomp{,~} || die
+
+ eautoreconf
+
+ # Restore depcomp
+ mv depcomp{~,} || die
+}
+
+multilib_src_configure() {
+ local myeconfargs=(
+ $(use_enable debug debug-output)
+ $(use_enable cxx)
+ $(use_enable static-libs static)
+ $(use_with alsa)
+ $(use_with jack)
+ $(use_with oss)
+ $(use_with sndio)
+ )
+
+ ECONF_SOURCE="${S}" econf "${myeconfargs[@]}"
+}
+
+multilib_src_compile() {
+ # workaround parallel build issue
+ emake lib/libportaudio.la
+ emake
+}
+
+src_compile() {
+ multilib-minimal_src_compile
+
+ if use doc; then
+ doxygen -u Doxyfile || die
+ doxygen Doxyfile || die
+ fi
+}
+
+multilib_src_install_all() {
+ einstalldocs
+ use doc && dodoc -r doc/html
+ find "${ED}" -name "*.la" -delete || die
+}