📄 pa_unix_oss.c
字号:
/*
* $Id: pa_unix_oss.c,v 1.6.2.22 2005/03/08 21:26:53 aknudsen Exp $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* OSS implementation by:
* Douglas Repetto
* Phil Burk
* Dominic Mazzoni
* Arve Knudsen
*
* 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 <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <unistd.h>
#include <pthread.h>
#include <alloca.h>
#include <malloc.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <limits.h>
#include <semaphore.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"
#include "pa_unix_util.h"
static int sysErr_;
static pthread_t mainThread_;
/* Check return value of system call, and map it to PaError */
#define ENSURE_(expr, code) \
do { \
if( UNLIKELY( (sysErr_ = (expr)) < 0 ) ) \
{ \
/* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
{ \
PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
} \
\
PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
result = (code); \
goto error; \
} \
} while( 0 );
#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
/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */
typedef struct
{
PaUtilHostApiRepresentation inheritedHostApiRep;
PaUtilStreamInterface callbackStreamInterface;
PaUtilStreamInterface blockingStreamInterface;
PaUtilAllocationGroup *allocations;
PaHostApiIndex hostApiIndex;
}
PaOSSHostApiRepresentation;
/** Per-direction structure for PaOssStream.
*
* Aspect StreamChannels: In case the user requests to open the same device for both capture and playback,
* but with different number of channels we will have to adapt between the number of user and host
* channels for at least one direction, since the configuration space is the same for both directions
* of an OSS device.
*/
typedef struct
{
int fd;
const char *devName;
int userChannelCount, hostChannelCount;
int userInterleaved;
void *buffer;
PaSampleFormat userFormat, hostFormat;
double latency;
unsigned long hostFrames, numBufs;
void **userBuffers; /* For non-interleaved blocking */
} PaOssStreamComponent;
/** Implementation specific representation of a PaStream.
*
*/
typedef struct PaOssStream
{
PaUtilStreamRepresentation streamRepresentation;
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
PaUtilBufferProcessor bufferProcessor;
PaUtilThreading threading;
int sharedDevice;
unsigned long framesPerHostBuffer;
int triggered; /* Have the devices been triggered yet (first start) */
int isActive;
int isStopped;
int lastPosPtr;
double lastStreamBytes;
int framesProcessed;
double sampleRate;
int callbackMode;
int callbackStop, callbackAbort;
PaOssStreamComponent *capture, *playback;
unsigned long pollTimeout;
sem_t semaphore;
}
PaOssStream;
typedef enum {
StreamMode_In,
StreamMode_Out
} StreamMode;
/* prototypes for functions declared in this file */
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 );
/** Initialize the OSS API implementation.
*
* This function will initialize host API datastructures and query host devices for information.
*
* Aspect DeviceCapabilities: Enumeration of host API devices is initiated from here
*
* Aspect FreeResources: If an error is encountered under way we have to free each resource allocated in this function,
* this happens with the usual "error" label.
*/
PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
PaOSSHostApiRepresentation *ossHostApi = NULL;
PA_UNLESS( ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ),
paInsufficientMemory );
PA_UNLESS( ossHostApi->allocations = PaUtil_CreateAllocationGroup(), paInsufficientMemory );
ossHostApi->hostApiIndex = hostApiIndex;
/* Initialize host API structure */
*hostApi = &ossHostApi->inheritedHostApiRep;
(*hostApi)->info.structVersion = 1;
(*hostApi)->info.type = paOSS;
(*hostApi)->info.name = "OSS";
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
PA_ENSURE( BuildDeviceList( ossHostApi ) );
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 );
mainThread_ = pthread_self();
return result;
error:
if( ossHostApi )
{
if( ossHostApi->allocations )
{
PaUtil_FreeAllAllocations( ossHostApi->allocations );
PaUtil_DestroyAllocationGroup( ossHostApi->allocations );
}
PaUtil_FreeMemory( ossHostApi );
}
return result;
}
PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name, PaHostApiIndex hostApiIndex, int maxInputChannels,
int maxOutputChannels, PaTime defaultLowInputLatency, PaTime defaultLowOutputLatency, PaTime defaultHighInputLatency,
PaTime defaultHighOutputLatency, double defaultSampleRate, PaUtilAllocationGroup *allocations )
{
PaError result = paNoError;
deviceInfo->structVersion = 2;
if( allocations )
{
size_t len = strlen( name ) + 1;
PA_UNLESS( deviceInfo->name = PaUtil_GroupAllocateMemory( allocations, len ), paInsufficientMemory );
strncpy( (char *)deviceInfo->name, name, len );
}
else
deviceInfo->name = name;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = maxInputChannels;
deviceInfo->maxOutputChannels = maxOutputChannels;
deviceInfo->defaultLowInputLatency = defaultLowInputLatency;
deviceInfo->defaultLowOutputLatency = defaultLowOutputLatency;
deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
deviceInfo->defaultSampleRate = defaultSampleRate;
error:
return result;
}
static PaError QueryDirection( const char *deviceName, StreamMode mode, double *defaultSampleRate, int *maxChannelCount,
double *defaultLowLatency, double *defaultHighLatency )
{
PaError result = paNoError;
int numChannels, maxNumChannels;
int busy = 0;
int devHandle = -1;
int sr;
*maxChannelCount = 0; /* Default value in case this fails */
if ( (devHandle = open( deviceName, (mode == StreamMode_In ? O_RDONLY : O_WRONLY) | O_NONBLOCK )) < 0 )
{
if( errno == EBUSY || errno == EAGAIN )
{
PA_DEBUG(( "%s: Device %s busy\n", __FUNCTION__, deviceName ));
}
else
{
PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
}
return paDeviceUnavailable;
}
/* 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;
if( ioctl( devHandle, SNDCTL_DSP_CHANNELS, &temp ) < 0 )
{
busy = EAGAIN == errno || EBUSY == errno;
/* ioctl() failed so bail out if we already have stereo */
if( maxNumChannels >= 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;
if( temp > maxNumChannels )
maxNumChannels = temp; /* Save maximum. */
}
}
/* A: We're able to open a device for capture if it's busy playing back and vice versa,
* but we can't configure anything */
if( 0 == maxNumChannels && busy )
{
result = paDeviceUnavailable;
goto error;
}
/* The above negotiation may fail for an old driver so try this older technique. */
if( maxNumChannels < 1 )
{
int stereo = 1;
if( ioctl( devHandle, SNDCTL_DSP_STEREO, &stereo ) < 0 )
{
maxNumChannels = 1;
}
else
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -