diff options
Diffstat (limited to 'pd/portaudio/pa_unix_oss')
-rw-r--r-- | pd/portaudio/pa_unix_oss/low_latency_tip.txt | bin | 0 -> 3111 bytes | |||
-rw-r--r-- | pd/portaudio/pa_unix_oss/pa_unix_oss.c | 1196 | ||||
-rw-r--r-- | pd/portaudio/pa_unix_oss/recplay.c | 114 |
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 Binary files differnew file mode 100644 index 00000000..2d982b79 --- /dev/null +++ b/pd/portaudio/pa_unix_oss/low_latency_tip.txt 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 ); +} |