📄 pa_unix.c
字号:
/*
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* Linux OSS Implementation by douglas repetto and Phil Burk
*
* Copyright (c) 1999-2000 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.
*
*/
/*
Modification History
1/2001 - Phil Burk - initial hack for Linux
2/2001 - Douglas Repetto - many improvements, initial query support
4/2/2001 - Phil - stop/abort thread control, separate in/out native buffers
5/28/2001 - Phil - use pthread_create() instead of clone(). Thanks Stephen Brandon!
use pthread_join() after thread shutdown.
5/29/2001 - Phil - query for multiple devices, multiple formats,
input mode and input+output mode working,
Pa_GetCPULoad() implemented.
PLB20010817 - Phil & Janos Haber - don't halt if test of sample rate fails.
SB20010904 - Stephen Brandon - mods needed for GNUSTEP and SndKit
JH20010905 - Janos Haber - FreeBSD mods
2001-09-22 - Heiko - (i.e. Heiko Purnhagen <purnhage@tnt.uni-hannover.de> ;-)
added 24k and 16k to ratesToTry[]
fixed Pa_GetInternalDevice()
changed DEVICE_NAME_BASE from /dev/audio to /dev/dsp
handled SNDCTL_DSP_SPEED in Pq_QueryDevice() more graceful
fixed Pa_StreamTime() for paqa_errs.c
fixed numCannel=2 oddity and error handling in Pa_SetupDeviceFormat()
grep also for HP20010922 ...
PLB20010924 - Phil - merged Heiko's changes
removed sNumDevices and potential related bugs,
use getenv("PA_MIN_LATENCY_MSEC") to set desired latency,
simplify CPU Load calculation by comparing real-time to framesPerBuffer,
always close device when querying even if error occurs,
PLB20010927 - Phil - Improved negotiation for numChannels.
SG20011005 - Stewart Greenhill - set numChannels back to reasonable value after query.
DH20010115 - David Herring - fixed uninitialized handle.
DM20020218 - Dominic Mazzoni - Try to open in nonblocking mode first, in case
the device is already open. New implementation of
Pa_StreamTime that uses SNDCTL_DSP_GETOPTR but
uses our own counter to avoid wraparound.
PLB20020222 - Phil Burk - Added WatchDog proc if audio running at high priority.
Check error return from read() and write().
Check CPU endianness instead of assuming Little Endian.
20020621 - pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by
Augustus Saunders. Return values from usleep() ignored by Sam Bayer
because not cross-platform compatible (at least until we get configure
going). Pa_SetupDeviceFormat split into input and output sides to
reflect capabilities of Solaris.
20030206 - Martin Rohrbach - various mods for Solaris
20030410 - Bjorn Dittmer-Roche - fixed numerous problems associated with pthread_t
20030630 - Thomas Richter - eliminated unused variable warnings.
TODO
O- put semaphore lock around shared data?
O- handle native formats better
O- handle stereo-only device better ???
O- what if input and output of a device capabilities differ (e.g. es1371) ???
*/
#include "pa_unix.h"
typedef void *(*pthread_function_t)(void *);
/************************************************* Shared Data ********/
/* FIXME - put Mutex around this shared data. */
static internalPortAudioDevice *sDeviceList = NULL;
static int sDefaultInputDeviceID = paNoDevice;
static int sDefaultOutputDeviceID = paNoDevice;
static int sPaHostError = 0;
/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/
static void Pa_StartUsageCalculation( internalPortAudioStream *past )
{
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return;
/* Query system timer for usage analysis and to prevent overuse of CPU. */
gettimeofday( &pahsc->pahsc_EntryTime, NULL );
}
static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB )
{
long secs = timeA->tv_sec - timeB->tv_sec;
long usecs = secs * 1000000;
usecs += (timeA->tv_usec - timeB->tv_usec);
return usecs;
}
/******************************************************************************
** Measure fractional CPU load based on real-time it took to calculate
** buffers worth of output.
*/
static void Pa_EndUsageCalculation( internalPortAudioStream *past )
{
struct timeval currentTime;
long usecsElapsed;
double newUsage;
#define LOWPASS_COEFFICIENT_0 (0.95)
#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return;
if( gettimeofday( ¤tTime, NULL ) == 0 )
{
usecsElapsed = SubtractTime_AminusB( ¤tTime, &pahsc->pahsc_EntryTime );
/* Use inverse because it is faster than the divide. */
newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerBuffer;
past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) +
(LOWPASS_COEFFICIENT_1 * newUsage);
}
}
/****************************************** END CPU UTILIZATION *******/
/*********************************************************************
* Determines the number of available devices by trying to open
* each "/dev/dsp#" or "/dsp/audio#" in order until it fails.
* Add each working device to a singly linked list of devices.
*/
PaError Pa_QueryDevices( void )
{
internalPortAudioDevice *pad, *lastPad;
int go = 1;
int numDevices = 0;
PaError testResult;
PaError result = paNoError;
char *envdev;
sDefaultInputDeviceID = paNoDevice;
sDefaultOutputDeviceID = paNoDevice;
lastPad = NULL;
while( go )
{
/* Allocate structure to hold device info. */
pad = (internalPortAudioDevice *)
PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
if( pad == NULL ) return paInsufficientMemory;
memset( pad, 0, sizeof(internalPortAudioDevice) );
/* Build name for device. */
if( numDevices == 0 )
{
sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE);
}
else
{
sprintf( pad->pad_DeviceName, DEVICE_NAME_BASE "%d", numDevices );
}
DBUG(("Try device %s\n", pad->pad_DeviceName ));
testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
DBUG(("Pa_QueryDevice returned %d\n", testResult ));
if( testResult != paNoError )
{
if( lastPad == NULL )
{
result = testResult; /* No good devices! */
}
go = 0;
PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
}
else
{
numDevices += 1;
/* Add to linked list of devices. */
if( lastPad )
{
lastPad->pad_Next = pad;
}
else
{
sDeviceList = pad; /* First element in linked list. */
}
lastPad = pad;
}
}
/* I'm sitting at a SunRay1 and I neither have /dev/audio# nor /dev/dsp#.
Instead, the correct audio device is stored in the environment variable
AUDIODEV and/or UTAUDIODEV, so check these devices as well if we haven't
checked them yet above - MR */
DBUG(("Checking for AUDIODEV and UTAUDIODEV\n"));
envdev = getenv("AUDIODEV");
if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE)) {
result = paNoError;
/* Allocate structure to hold device info. */
pad = (internalPortAudioDevice *)
PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
if( pad == NULL ) return paInsufficientMemory;
memset( pad, 0, sizeof(internalPortAudioDevice) );
/* Build name for device. */
strcpy(pad->pad_DeviceName, envdev);
DBUG(("Try device %s\n", pad->pad_DeviceName ));
testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
DBUG(("Pa_QueryDevice returned %d\n", testResult ));
if( testResult != paNoError )
{
if( lastPad == NULL )
{
result = testResult; /* No good devices! */
}
PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
}
else
{
numDevices += 1;
/* Add to linked list of devices. */
if( lastPad )
{
lastPad->pad_Next = pad;
}
else
{
sDeviceList = pad; /* First element in linked list. */
}
lastPad = pad;
}
}
envdev = getenv("UTAUDIODEV");
if (envdev != NULL && !strstr(envdev, DEVICE_NAME_BASE) && getenv("AUDIODEV") != NULL && strcmp(envdev, getenv("AUDIODEV"))) {
result = paNoError;
/* Allocate structure to hold device info. */
pad = (internalPortAudioDevice *)
PaHost_AllocateFastMemory( sizeof(internalPortAudioDevice) );
if( pad == NULL ) return paInsufficientMemory;
memset( pad, 0, sizeof(internalPortAudioDevice) );
/* Build name for device. */
strcpy(pad->pad_DeviceName, envdev);
DBUG(("Try device %s\n", pad->pad_DeviceName ));
testResult = Pa_QueryDevice( pad->pad_DeviceName, pad );
DBUG(("Pa_QueryDevice returned %d\n", testResult ));
if( testResult != paNoError )
{
if( lastPad == NULL )
{
result = testResult; /* No good devices! */
}
PaHost_FreeFastMemory( pad, sizeof(internalPortAudioDevice) );
}
else
{
numDevices += 1;
/* Add to linked list of devices. */
if( lastPad )
{
lastPad->pad_Next = pad;
}
else
{
sDeviceList = pad; /* First element in linked list. */
}
lastPad = pad;
}
}
return result;
}
/*************************************************************************/
int Pa_CountDevices()
{
int numDevices = 0;
internalPortAudioDevice *pad;
if( sDeviceList == NULL ) Pa_Initialize();
/* Count devices in list. */
pad = sDeviceList;
while( pad != NULL )
{
pad = pad->pad_Next;
numDevices++;
}
return numDevices;
}
/*************************************************************************/
internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id )
{
internalPortAudioDevice *pad;
if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
pad = sDeviceList;
while( id > 0 )
{
pad = pad->pad_Next;
id--;
}
return pad;
}
/*************************************************************************/
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
{
internalPortAudioDevice *pad;
if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL;
pad = Pa_GetInternalDevice( id );
return &pad->pad_Info ;
}
static PaError Pa_MaybeQueryDevices( void )
{
if( sDeviceList == NULL )
{
return Pa_QueryDevices();
}
return 0;
}
PaDeviceID Pa_GetDefaultInputDeviceID( void )
{
/* return paNoDevice; */
return 0;
}
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
{
return 0;
}
/**********************************************************************
** Make sure that we have queried the device capabilities.
*/
PaError PaHost_Init( void )
{
return Pa_MaybeQueryDevices();
}
/*******************************************************************************************
* The ol' Canary in a Coal Mine trick.
* Just update the time periodically.
* Runs at low priority so if audio thread runs wild, this thread will get starved
* and the watchdog will detect it.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -