aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio/pa_unix_oss
diff options
context:
space:
mode:
Diffstat (limited to 'pd/portaudio/pa_unix_oss')
-rw-r--r--pd/portaudio/pa_unix_oss/low_latency_tip.txtbin0 -> 3111 bytes
-rw-r--r--pd/portaudio/pa_unix_oss/pa_unix_oss.c1196
-rw-r--r--pd/portaudio/pa_unix_oss/recplay.c114
3 files changed, 1310 insertions, 0 deletions
diff --git a/pd/portaudio/pa_unix_oss/low_latency_tip.txt b/pd/portaudio/pa_unix_oss/low_latency_tip.txt
new file mode 100644
index 00000000..2d982b79
--- /dev/null
+++ b/pd/portaudio/pa_unix_oss/low_latency_tip.txt
Binary files differ
diff --git a/pd/portaudio/pa_unix_oss/pa_unix_oss.c b/pd/portaudio/pa_unix_oss/pa_unix_oss.c
new file mode 100644
index 00000000..b2e6d5a4
--- /dev/null
+++ b/pd/portaudio/pa_unix_oss/pa_unix_oss.c
@@ -0,0 +1,1196 @@
+/*
+ * $Id: pa_unix_oss.c,v 1.6.2.7 2003/09/23 21:17:22 rossbencina Exp $
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com
+ * OSS implementation by:
+ * Douglas Repetto
+ * Phil Burk
+ * Dominic Mazzoni
+ *
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#ifdef __linux__
+# include <linux/soundcard.h>
+# define DEVICE_NAME_BASE "/dev/dsp"
+#else
+# include <machine/soundcard.h> /* JH20010905 */
+# define DEVICE_NAME_BASE "/dev/audio"
+#endif
+
+#include "portaudio.h"
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+
+/* TODO: add error text handling
+#define PA_UNIX_OSS_ERROR( errorCode, errorText ) \
+ PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
+*/
+
+#define PRINT(x) { printf x; fflush(stdout); }
+#define DBUG(x) /* PRINT(x) */
+
+/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ PaHostApiIndex hostApiIndex;
+}
+PaOSSHostApiRepresentation;
+
+typedef struct PaOSS_DeviceList {
+ PaDeviceInfo *deviceInfo;
+ struct PaOSS_DeviceList *next;
+}
+PaOSS_DeviceList;
+
+/* prototypes for functions declared in this file */
+
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex );
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
+static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi );
+
+
+PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ PaOSSHostApiRepresentation *ossHostApi;
+
+ DBUG(("PaOSS_Initialize\n"));
+
+ ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) );
+ if( !ossHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ ossHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !ossHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &ossHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paOSS;
+ (*hostApi)->info.name = "OSS";
+ ossHostApi->hostApiIndex = hostApiIndex;
+
+ BuildDeviceList( ossHostApi );
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable,
+ PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( ossHostApi )
+ {
+ if( ossHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( ossHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( ossHostApi );
+ }
+ return result;
+}
+
+#ifndef AFMT_S16_NE
+#define AFMT_S16_NE Get_AFMT_S16_NE()
+/*********************************************************************
+ * Some versions of OSS do not define AFMT_S16_NE. So check CPU.
+ * PowerPC is Big Endian. X86 is Little Endian.
+ */
+static int Get_AFMT_S16_NE( void )
+{
+ long testData = 1;
+ char *ptr = (char *) &testData;
+ int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */
+ return isLittle ? AFMT_S16_LE : AFMT_S16_BE;
+}
+#endif
+
+PaError PaOSS_SetFormat(const char *callingFunctionName, int deviceHandle,
+ char *deviceName, int inputChannelCount, int outputChannelCount,
+ double *sampleRate)
+{
+ int format;
+ int rate;
+ int temp;
+
+ /* Attempt to set format to 16-bit */
+
+ format = AFMT_S16_NE;
+ if (ioctl(deviceHandle, SNDCTL_DSP_SETFMT, &format) == -1) {
+ DBUG(("%s: could not set format: %s\n", callingFunctionName, deviceName ));
+ return paSampleFormatNotSupported;
+ }
+ if (format != AFMT_S16_NE) {
+ DBUG(("%s: device does not support AFMT_S16_NE: %s\n", callingFunctionName, deviceName ));
+ return paSampleFormatNotSupported;
+ }
+
+ /* try to set the number of channels */
+
+ if (inputChannelCount > 0) {
+ temp = inputChannelCount;
+
+ if( ioctl(deviceHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 ) {
+ DBUG(("%s: Couldn't set device %s to %d channels\n", callingFunctionName, deviceName, inputChannelCount ));
+ return paSampleFormatNotSupported;
+ }
+ }
+
+ if (outputChannelCount > 0) {
+ temp = outputChannelCount;
+
+ if( ioctl(deviceHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 ) {
+ DBUG(("%s: Couldn't set device %s to %d channels\n", callingFunctionName, deviceName, outputChannelCount ));
+ return paSampleFormatNotSupported;
+ }
+ }
+
+ /* try to set the sample rate */
+
+ rate = (int)(*sampleRate);
+ if (ioctl(deviceHandle, SNDCTL_DSP_SPEED, &rate) == -1)
+ {
+ DBUG(("%s: Device %s, couldn't set sample rate to %d\n",
+ callingFunctionName, deviceName, (int)*sampleRate ));
+ return paInvalidSampleRate;
+ }
+
+ /* reject if there's no sample rate within 1% of the one requested */
+ if ((fabs(*sampleRate - rate) / *sampleRate) > 0.01)
+ {
+ DBUG(("%s: Device %s, wanted %d, closest sample rate was %d\n",
+ callingFunctionName, deviceName, (int)*sampleRate, rate ));
+ return paInvalidSampleRate;
+ }
+
+ *sampleRate = rate;
+
+ return paNoError;
+}
+
+static PaError PaOSS_QueryDevice(char *deviceName, PaDeviceInfo *deviceInfo)
+{
+ PaError result = paNoError;
+ int tempDevHandle;
+ int numChannels, maxNumChannels;
+ int sampleRate;
+ int format;
+
+ /* douglas:
+ we have to do this querying in a slightly different order. apparently
+ some sound cards will give you different info based on their settins.
+ e.g. a card might give you stereo at 22kHz but only mono at 44kHz.
+ the correct order for OSS is: format, channels, sample rate
+ */
+
+ if ( (tempDevHandle = open(deviceName,O_WRONLY|O_NONBLOCK)) == -1 )
+ {
+ DBUG(("PaOSS_QueryDevice: could not open %s\n", deviceName ));
+ return paDeviceUnavailable;
+ }
+
+ /* Attempt to set format to 16-bit */
+ format = AFMT_S16_NE;
+ if (ioctl(tempDevHandle, SNDCTL_DSP_SETFMT, &format) == -1) {
+ DBUG(("PaOSS_QueryDevice: could not set format: %s\n", deviceName ));
+ result = paSampleFormatNotSupported;
+ goto error;
+ }
+ if (format != AFMT_S16_NE) {
+ DBUG(("PaOSS_QueryDevice: device does not support AFMT_S16_NE: %s\n", deviceName ));
+ result = paSampleFormatNotSupported;
+ goto error;
+ }
+
+ /* Negotiate for the maximum number of channels for this device. PLB20010927
+ * Consider up to 16 as the upper number of channels.
+ * Variable maxNumChannels should contain the actual upper limit after the call.
+ * Thanks to John Lazzaro and Heiko Purnhagen for suggestions.
+ */
+ maxNumChannels = 0;
+ for( numChannels = 1; numChannels <= 16; numChannels++ )
+ {
+ int temp = numChannels;
+ DBUG(("PaOSS_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels ))
+ if(ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 )
+ {
+ /* ioctl() failed so bail out if we already have stereo */
+ if( numChannels > 2 ) break;
+ }
+ else
+ {
+ /* ioctl() worked but bail out if it does not support numChannels.
+ * We don't want to leave gaps in the numChannels supported.
+ */
+ if( (numChannels > 2) && (temp != numChannels) ) break;
+ DBUG(("PaOSS_QueryDevice: temp = %d\n", temp ))
+ if( temp > maxNumChannels ) maxNumChannels = temp; /* Save maximum. */
+ }
+ }
+
+ /* The above negotiation may fail for an old driver so try this older technique. */
+ if( maxNumChannels < 1 )
+ {
+ int stereo = 1;
+ if(ioctl(tempDevHandle, SNDCTL_DSP_STEREO, &stereo) < 0)
+ {
+ maxNumChannels = 1;
+ }
+ else
+ {
+ maxNumChannels = (stereo) ? 2 : 1;
+ }
+ DBUG(("PaOSS_QueryDevice: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", maxNumChannels ))
+ }
+
+ DBUG(("PaOSS_QueryDevice: maxNumChannels = %d\n", maxNumChannels))
+
+ deviceInfo->maxOutputChannels = maxNumChannels;
+ /* FIXME - for now, assume maxInputChannels = maxOutputChannels.
+ * Eventually do separate queries for O_WRONLY and O_RDONLY
+ */
+ deviceInfo->maxInputChannels = deviceInfo->maxOutputChannels;
+
+ /* During channel negotiation, the last ioctl() may have failed. This can
+ * also cause sample rate negotiation to fail. Hence the following, to return
+ * to a supported number of channels. SG20011005 */
+ {
+ int temp = maxNumChannels;
+ if( temp > 2 ) temp = 2; /* use most reasonable default value */
+ ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp);
+ }
+
+ /* Get supported sample rate closest to 44100 Hz */
+ sampleRate = 44100;
+ if (ioctl(tempDevHandle, SNDCTL_DSP_SPEED, &sampleRate) == -1)
+ {
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+
+ deviceInfo->defaultSampleRate = sampleRate;
+
+ deviceInfo->structVersion = 2;
+
+ /* TODO */
+ deviceInfo->defaultLowInputLatency = 128.0 / sampleRate;
+ deviceInfo->defaultLowOutputLatency = 128.0 / sampleRate;
+ deviceInfo->defaultHighInputLatency = 16384.0 / sampleRate;
+ deviceInfo->defaultHighOutputLatency = 16384.0 / sampleRate;
+
+ result = paNoError;
+
+error:
+ /* We MUST close the handle here or we won't be able to reopen it later!!! */
+ close(tempDevHandle);
+
+ return result;
+}
+
+static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
+{
+ PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep;
+ PaOSS_DeviceList *head = NULL, *tail = NULL, *entry;
+ int i;
+ int numDevices;
+
+ /* Find devices by calling PaOSS_QueryDevice on each
+ potential device names. When we find a valid one,
+ add it to a linked list. */
+
+ for(i=0; i<10; i++) {
+ char deviceName[32];
+ PaDeviceInfo deviceInfo;
+ int testResult;
+
+ if (i==0)
+ sprintf(deviceName, "%s", DEVICE_NAME_BASE);
+ else
+ sprintf(deviceName, "%s%d", DEVICE_NAME_BASE, i);
+
+ DBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName ));
+ testResult = PaOSS_QueryDevice(deviceName, &deviceInfo);
+ DBUG(("PaOSS BuildDeviceList: PaOSS_QueryDevice returned %d\n", testResult ));
+
+ if (testResult == paNoError) {
+ DBUG(("PaOSS BuildDeviceList: Adding device %s to list\n", deviceName));
+ deviceInfo.hostApi = ossApi->hostApiIndex;
+ deviceInfo.name = PaUtil_GroupAllocateMemory(
+ ossApi->allocations, strlen(deviceName)+1);
+ strcpy((char *)deviceInfo.name, deviceName);
+ entry = (PaOSS_DeviceList *)PaUtil_AllocateMemory(sizeof(PaOSS_DeviceList));
+ entry->deviceInfo = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
+ ossApi->allocations, sizeof(PaDeviceInfo) );
+ entry->next = NULL;
+ memcpy(entry->deviceInfo, &deviceInfo, sizeof(PaDeviceInfo));
+ if (tail)
+ tail->next = entry;
+ else {
+ head = entry;
+ tail = entry;
+ }
+ }
+ }
+
+ /* Make an array of PaDeviceInfo pointers out of the linked list */
+
+ numDevices = 0;
+ entry = head;
+ while(entry) {
+ numDevices++;
+ entry = entry->next;
+ }
+
+ DBUG(("PaOSS BuildDeviceList: Total number of devices found: %d\n", numDevices));
+
+ commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ ossApi->allocations, sizeof(PaDeviceInfo*) *numDevices );
+
+ entry = head;
+ i = 0;
+ while(entry) {
+ commonApi->deviceInfos[i] = entry->deviceInfo;
+ i++;
+ entry = entry->next;
+ }
+
+ commonApi->info.deviceCount = numDevices;
+ commonApi->info.defaultInputDevice = 0;
+ commonApi->info.defaultOutputDevice = 0;
+
+ return paNoError;
+}
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+
+ if( ossHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( ossHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( ossHostApi );
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaDeviceIndex device;
+ PaDeviceInfo *deviceInfo;
+ PaError result = paNoError;
+ char *deviceName;
+ int inputChannelCount, outputChannelCount;
+ int tempDevHandle = 0;
+ int flags;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ if (inputChannelCount == 0 && outputChannelCount == 0)
+ return paInvalidChannelCount;
+
+ /* if full duplex, make sure that they're the same device */
+
+ if (inputChannelCount > 0 && outputChannelCount > 0 &&
+ inputParameters->device != outputParameters->device)
+ return paInvalidDevice;
+
+ /* if full duplex, also make sure that they're the same number of channels */
+
+ if (inputChannelCount > 0 && outputChannelCount > 0 &&
+ inputChannelCount != outputChannelCount)
+ return paInvalidChannelCount;
+
+ /* open the device so we can do more tests */
+
+ if (inputChannelCount > 0) {
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi);
+ if (result != paNoError)
+ return result;
+ }
+ else {
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi);
+ if (result != paNoError)
+ return result;
+ }
+
+ deviceInfo = hostApi->deviceInfos[device];
+ deviceName = (char *)deviceInfo->name;
+
+ flags = O_NONBLOCK;
+ if (inputChannelCount > 0 && outputChannelCount > 0)
+ flags |= O_RDWR;
+ else if (inputChannelCount > 0)
+ flags |= O_RDONLY;
+ else
+ flags |= O_WRONLY;
+
+ if ( (tempDevHandle = open(deviceInfo->name, flags)) == -1 )
+ {
+ DBUG(("PaOSS IsFormatSupported: could not open %s\n", deviceName ));
+ return paDeviceUnavailable;
+ }
+
+ /* PaOSS_SetFormat will do the rest of the checking for us */
+
+ if ((result = PaOSS_SetFormat("PaOSS IsFormatSupported", tempDevHandle,
+ deviceName, inputChannelCount, outputChannelCount,
+ &sampleRate)) != paNoError)
+ {
+ goto error;
+ }
+
+ /* everything succeeded! */
+
+ close(tempDevHandle);
+
+ return paFormatIsSupported;
+
+ error:
+ if (tempDevHandle)
+ close(tempDevHandle);
+
+ return paSampleFormatNotSupported;
+}
+
+/* PaOSSStream - a stream data structure specifically for this implementation */
+
+typedef struct PaOSSStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ int deviceHandle;
+
+ int stopSoon;
+ int stopNow;
+ int isActive;
+
+ int inputChannelCount;
+ int outputChannelCount;
+
+ pthread_t thread;
+
+ void *inputBuffer;
+ void *outputBuffer;
+
+ int lastPosPtr;
+ double lastStreamBytes;
+
+ int framesProcessed;
+
+ double sampleRate;
+
+ unsigned long framesPerHostCallback;
+}
+PaOSSStream;
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi;
+ PaOSSStream *stream = 0;
+ PaDeviceIndex device;
+ PaDeviceInfo *deviceInfo;
+ audio_buf_info bufinfo;
+ int bytesPerHostBuffer;
+ int flags;
+ int deviceHandle = 0;
+ char *deviceName;
+ unsigned long framesPerHostBuffer;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat = paInt16, outputSampleFormat = paInt16;
+ PaSampleFormat hostInputSampleFormat = paInt16, hostOutputSampleFormat = paInt16;
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16, inputSampleFormat );
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support inputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( paInt16, outputSampleFormat );
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ if( inputChannelCount == 0 && outputChannelCount == 0 )
+ {
+ DBUG(("Both inputChannelCount and outputChannelCount are zero!\n"));
+ return paUnanticipatedHostError;
+ }
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+ /*
+ * open the device and set parameters here
+ */
+
+ if (inputChannelCount == 0 && outputChannelCount == 0)
+ return paInvalidChannelCount;
+
+ /* if full duplex, make sure that they're the same device */
+
+ if (inputChannelCount > 0 && outputChannelCount > 0 &&
+ inputParameters->device != outputParameters->device)
+ return paInvalidDevice;
+
+ /* if full duplex, also make sure that they're the same number of channels */
+
+ if (inputChannelCount > 0 && outputChannelCount > 0 &&
+ inputChannelCount != outputChannelCount)
+ return paInvalidChannelCount;
+
+ /* note that inputParameters and outputParameters device indicies are
+ * already in host format */
+ device = (inputChannelCount > 0 )
+ ? inputParameters->device
+ : outputParameters->device;
+
+ deviceInfo = hostApi->deviceInfos[device];
+ deviceName = (char *)deviceInfo->name;
+
+ flags = O_NONBLOCK;
+ if (inputChannelCount > 0 && outputChannelCount > 0)
+ flags |= O_RDWR;
+ else if (inputChannelCount > 0)
+ flags |= O_RDONLY;
+ else
+ flags |= O_WRONLY;
+
+ /* open first in nonblocking mode, in case it's busy... */
+ if ( (deviceHandle = open(deviceInfo->name, flags)) == -1 )
+ {
+ DBUG(("PaOSS OpenStream: could not open %s\n", deviceName ));
+ return paDeviceUnavailable;
+ }
+
+ /* if that succeeded, immediately open it again in blocking mode */
+ close(deviceHandle);
+ flags -= O_NONBLOCK;
+ if ( (deviceHandle = open(deviceInfo->name, flags)) == -1 )
+ {
+ DBUG(("PaOSS OpenStream: could not open %s in blocking mode\n", deviceName ));
+ return paDeviceUnavailable;
+ }
+
+ if ((result = PaOSS_SetFormat("PaOSS OpenStream", deviceHandle,
+ deviceName, inputChannelCount, outputChannelCount,
+ &sampleRate)) != paNoError)
+ {
+ goto error;
+ }
+
+ /* Compute number of frames per host buffer - if we can't retrieve the
+ * value, use the user's value instead
+ */
+
+ if ( ioctl(deviceHandle, SNDCTL_DSP_GETBLKSIZE, &bytesPerHostBuffer) == 0)
+ {
+ framesPerHostBuffer = bytesPerHostBuffer / 2 / (inputChannelCount>0? inputChannelCount: outputChannelCount);
+ }
+ else
+ framesPerHostBuffer = framesPerBuffer;
+
+ /* Allocate stream and fill in structure */
+
+ stream = (PaOSSStream*)PaUtil_AllocateMemory( sizeof(PaOSSStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &ossHostApi->callbackStreamInterface, streamCallback, userData );
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &ossHostApi->blockingStreamInterface, streamCallback, userData );
+ }
+
+ stream->streamRepresentation.streamInfo.inputLatency = 0.;
+ stream->streamRepresentation.streamInfo.outputLatency = 0.;
+
+ if (inputChannelCount > 0) {
+ if (ioctl( deviceHandle, SNDCTL_DSP_GETISPACE, &bufinfo) == 0)
+ stream->streamRepresentation.streamInfo.inputLatency =
+ (bufinfo.fragsize * bufinfo.fragstotal) / sampleRate;
+ }
+
+ if (outputChannelCount > 0) {
+ if (ioctl( deviceHandle, SNDCTL_DSP_GETOSPACE, &bufinfo) == 0)
+ stream->streamRepresentation.streamInfo.outputLatency =
+ (bufinfo.fragsize * bufinfo.fragstotal) / sampleRate;
+ }
+
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+ /* we assume a fixed host buffer size in this example, but the buffer processor
+ can also support bounded and unknown host buffer sizes by passing
+ paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
+ paUtilFixedHostBufferSize below. */
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerHostBuffer, paUtilFixedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError )
+ goto error;
+
+ stream->framesPerHostCallback = framesPerHostBuffer;
+
+ stream->stopSoon = 0;
+ stream->stopNow = 0;
+ stream->isActive = 0;
+ stream->thread = 0;
+ stream->lastPosPtr = 0;
+ stream->lastStreamBytes = 0;
+ stream->sampleRate = sampleRate;
+ stream->framesProcessed = 0;
+ stream->deviceHandle = deviceHandle;
+
+ if (inputChannelCount > 0)
+ stream->inputBuffer = PaUtil_AllocateMemory( 2 * framesPerHostBuffer * inputChannelCount );
+ else
+ stream->inputBuffer = NULL;
+
+ if (outputChannelCount > 0)
+ stream->outputBuffer = PaUtil_AllocateMemory( 2 * framesPerHostBuffer * outputChannelCount );
+ else
+ stream->outputBuffer = NULL;
+
+ stream->inputChannelCount = inputChannelCount;
+ stream->outputChannelCount = outputChannelCount;
+
+ *s = (PaStream*)stream;
+
+ result = paNoError;
+
+ return result;
+
+error:
+ if( stream )
+ PaUtil_FreeMemory( stream );
+
+ if( deviceHandle )
+ close( deviceHandle );
+
+ return result;
+}
+
+static void *PaOSS_AudioThreadProc(void *userData)
+{
+ PaOSSStream *stream = (PaOSSStream*)userData;
+
+ DBUG(("PaOSS AudioThread: %d in, %d out\n", stream->inputChannelCount, stream->outputChannelCount));
+
+ while( (stream->stopNow == 0) && (stream->stopSoon == 0) ) {
+ PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */
+ int callbackResult;
+ unsigned long framesProcessed;
+ int bytesRequested;
+ int bytesRead, bytesWritten;
+ int delta;
+ int result;
+ count_info info;
+
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+
+ PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo,
+ 0 /* @todo pass underflow/overflow flags when necessary */ );
+
+ /*
+ depending on whether the host buffers are interleaved, non-interleaved
+ or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
+ PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
+ */
+
+ if ( stream->inputChannelCount > 0 )
+ {
+ bytesRequested = stream->framesPerHostCallback * 2 * stream->inputChannelCount;
+ bytesRead = read( stream->deviceHandle, stream->inputBuffer, bytesRequested );
+
+ PaUtil_SetInputFrameCount( &stream->bufferProcessor, bytesRead/(2*stream->inputChannelCount));
+ PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
+ 0, /* first channel of inputBuffer is channel 0 */
+ stream->inputBuffer,
+ 0 ); /* 0 - use inputChannelCount passed to init buffer processor */
+ }
+
+ if ( stream->outputChannelCount > 0 )
+ {
+ PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
+ PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
+ 0, /* first channel of outputBuffer is channel 0 */
+ stream->outputBuffer,
+ 0 ); /* 0 - use outputChannelCount passed to init buffer processor */
+ }
+
+ callbackResult = paContinue;
+ framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
+
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+
+ if( callbackResult == paContinue )
+ {
+ /* nothing special to do */
+ }
+ else if( callbackResult == paAbort )
+ {
+ /* once finished, call the finished callback */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+ return NULL; /* return from the loop */
+ }
+ else if ( callbackResult == paComplete )
+ {
+ /* User callback has asked us to stop with paComplete or other non-zero value */
+
+ /* once finished, call the finished callback */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+
+ stream->stopSoon = 1;
+ }
+
+ if ( stream->outputChannelCount > 0 ) {
+ /* write output samples AFTER we've checked the callback result code */
+
+ bytesRequested = stream->framesPerHostCallback * 2 * stream->outputChannelCount;
+ bytesWritten = write( stream->deviceHandle, stream->outputBuffer, bytesRequested );
+
+ /* TODO: handle bytesWritten != bytesRequested (slippage?) */
+ }
+
+ /* Update current stream time (using a double so that
+ we don't wrap around like info.bytes does) */
+ if( stream->outputChannelCount > 0 )
+ result = ioctl( stream->deviceHandle, SNDCTL_DSP_GETOPTR, &info);
+ else
+ result = ioctl( stream->deviceHandle, SNDCTL_DSP_GETIPTR, &info);
+
+ if (result == 0) {
+ delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
+ stream->lastStreamBytes += delta;
+ stream->lastPosPtr = info.bytes;
+ }
+
+ stream->framesProcessed += stream->framesPerHostCallback;
+ }
+
+ return NULL;
+}
+
+/*
+ When CloseStream() is called, the multi-api layer ensures that
+ the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaOSSStream *stream = (PaOSSStream*)s;
+
+ close(stream->deviceHandle);
+
+ if ( stream->inputBuffer )
+ PaUtil_FreeMemory( stream->inputBuffer );
+ if ( stream->outputBuffer )
+ PaUtil_FreeMemory( stream->outputBuffer );
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ PaUtil_FreeMemory( stream );
+
+ return result;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaOSSStream *stream = (PaOSSStream*)s;
+ int presult;
+
+ stream->isActive = 1;
+ stream->lastPosPtr = 0;
+ stream->lastStreamBytes = 0;
+ stream->framesProcessed = 0;
+
+ DBUG(("PaOSS StartStream\n"));
+
+ /* only use the thread for callback streams */
+ if( stream->bufferProcessor.streamCallback ) {
+ presult = pthread_create(&stream->thread,
+ NULL /*pthread_attr_t * attr*/,
+ (void*)PaOSS_AudioThreadProc, (void *)stream);
+ }
+
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaOSSStream *stream = (PaOSSStream*)s;
+
+ stream->stopSoon = 1;
+
+ /* only use the thread for callback streams */
+ if( stream->bufferProcessor.streamCallback )
+ pthread_join( stream->thread, NULL );
+
+ stream->stopSoon = 0;
+ stream->stopNow = 0;
+ stream->isActive = 0;
+
+ DBUG(("PaOSS StopStream: Stopped stream\n"));
+
+ return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaOSSStream *stream = (PaOSSStream*)s;
+
+ stream->stopNow = 1;
+
+ /* only use the thread for callback streams */
+ if( stream->bufferProcessor.streamCallback )
+ pthread_join( stream->thread, NULL );
+
+ stream->stopSoon = 0;
+ stream->stopNow = 0;
+ stream->isActive = 0;
+
+ DBUG(("PaOSS AbortStream: Stopped stream\n"));
+
+ return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaOSSStream *stream = (PaOSSStream*)s;
+
+ return (!stream->isActive);
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaOSSStream *stream = (PaOSSStream*)s;
+
+ return (stream->isActive);
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ PaOSSStream *stream = (PaOSSStream*)s;
+ count_info info;
+ int delta;
+
+ if( stream->outputChannelCount > 0 ) {
+ if (ioctl( stream->deviceHandle, SNDCTL_DSP_GETOPTR, &info) == 0) {
+ delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF;
+ return ( stream->lastStreamBytes + delta) / ( stream->outputChannelCount * 2 ) / stream->sampleRate;
+ }
+ }
+ else {
+ if (ioctl( stream->deviceHandle, SNDCTL_DSP_GETIPTR, &info) == 0) {
+ delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF;
+ return ( stream->lastStreamBytes + delta) / ( stream->inputChannelCount * 2 ) / stream->sampleRate;
+ }
+ }
+
+ /* the ioctl failed, but we can still give a coarse estimate */
+
+ return stream->framesProcessed / stream->sampleRate;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaOSSStream *stream = (PaOSSStream*)s;
+
+ return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+}
+
+
+/*
+ As separate stream interfaces are used for blocking and callback
+ streams, the following functions can be guaranteed to only be called
+ for blocking streams.
+*/
+
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaOSSStream *stream = (PaOSSStream*)s;
+ int bytesRequested, bytesRead;
+
+ bytesRequested = frames * 2 * stream->inputChannelCount;
+ bytesRead = read( stream->deviceHandle, stream->inputBuffer, bytesRequested );
+
+ if ( bytesRequested != bytesRead )
+ return paUnanticipatedHostError;
+ else
+ return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaOSSStream *stream = (PaOSSStream*)s;
+ int bytesRequested, bytesWritten;
+
+ bytesRequested = frames * 2 * stream->outputChannelCount;
+ bytesWritten = write( stream->deviceHandle, buffer, bytesRequested );
+
+ if ( bytesRequested != bytesWritten )
+ return paUnanticipatedHostError;
+ else
+ return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaOSSStream *stream = (PaOSSStream*)s;
+ audio_buf_info info;
+
+ if ( ioctl(stream->deviceHandle, SNDCTL_DSP_GETISPACE, &info) == 0)
+ {
+ int bytesAvailable = info.fragments * info.fragsize;
+ return ( bytesAvailable / 2 / stream->inputChannelCount );
+ }
+ else
+ return 0; /* TODO: is this right for "don't know"? */
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaOSSStream *stream = (PaOSSStream*)s;
+
+ audio_buf_info info;
+
+ if ( ioctl(stream->deviceHandle, SNDCTL_DSP_GETOSPACE, &info) == 0)
+ {
+ int bytesAvailable = info.fragments * info.fragsize;
+ return ( bytesAvailable / 2 / stream->outputChannelCount );
+ }
+ else
+ return 0; /* TODO: is this right for "don't know"? */
+}
+
diff --git a/pd/portaudio/pa_unix_oss/recplay.c b/pd/portaudio/pa_unix_oss/recplay.c
new file mode 100644
index 00000000..9d4c78cf
--- /dev/null
+++ b/pd/portaudio/pa_unix_oss/recplay.c
@@ -0,0 +1,114 @@
+/*
+ * recplay.c
+ * Phil Burk
+ * Minimal record and playback test.
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#ifndef __STDC__
+/* #include <getopt.h> */
+#endif /* __STDC__ */
+#include <fcntl.h>
+#ifdef __STDC__
+#include <string.h>
+#else /* __STDC__ */
+#include <strings.h>
+#endif /* __STDC__ */
+#include <sys/soundcard.h>
+
+#define NUM_BYTES (64*1024)
+#define BLOCK_SIZE (4*1024)
+
+#define AUDIO "/dev/dsp"
+
+char buffer[NUM_BYTES];
+
+int audioDev = 0;
+
+main (int argc, char *argv[])
+{
+ int numLeft;
+ char *ptr;
+ int num;
+ int samplesize;
+
+ /********** RECORD ********************/
+ /* Open audio device. */
+ audioDev = open (AUDIO, O_RDONLY, 0);
+ if (audioDev == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+
+ /* Set to 16 bit samples. */
+ samplesize = 16;
+ ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
+ if (samplesize != 16)
+ {
+ perror("Unable to set the sample size.");
+ exit(-1);
+ }
+
+ /* Record in blocks */
+ printf("Begin recording.\n");
+ numLeft = NUM_BYTES;
+ ptr = buffer;
+ while( numLeft >= BLOCK_SIZE )
+ {
+ if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 )
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ else
+ {
+ printf("Read %d bytes\n", num);
+ ptr += num;
+ numLeft -= num;
+ }
+ }
+
+ close( audioDev );
+
+ /********** PLAYBACK ********************/
+ /* Open audio device for writing. */
+ audioDev = open (AUDIO, O_WRONLY, 0);
+ if (audioDev == -1)
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+
+ /* Set to 16 bit samples. */
+ samplesize = 16;
+ ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
+ if (samplesize != 16)
+ {
+ perror("Unable to set the sample size.");
+ exit(-1);
+ }
+
+ /* Play in blocks */
+ printf("Begin playing.\n");
+ numLeft = NUM_BYTES;
+ ptr = buffer;
+ while( numLeft >= BLOCK_SIZE )
+ {
+ if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 )
+ {
+ perror (AUDIO);
+ exit (-1);
+ }
+ else
+ {
+ printf("Wrote %d bytes\n", num);
+ ptr += num;
+ numLeft -= num;
+ }
+ }
+
+ close( audioDev );
+}