📄 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_tTODOO- put semaphore lock around shared data?O- handle native formats betterO- 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -