📄 pa_unix_oss.c
字号:
/* * $Id: pa_unix_oss.c,v 1.2 2004/04/22 04:19:51 mbrubeck 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, 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;}#endifPaError 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -