From ae6b5d89ea93b95c2990895077cf5e8f0bba9ad9 Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Mon, 2 Feb 2004 11:28:02 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r1301, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=1302 --- pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c | 385 +++++++++++++++++++++++++++++ 1 file changed, 385 insertions(+) create mode 100644 pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c (limited to 'pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c') diff --git a/pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c b/pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c new file mode 100644 index 00000000..386cd75b --- /dev/null +++ b/pd/portaudio_v18/pa_unix_oss/pa_unix_oss.c @@ -0,0 +1,385 @@ +/* + * 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: + 20020621: pa_unix_oss.c split into pa_unix.c, pa_unix.h, pa_unix_oss.c by + Augustus Saunders. See pa_unix.c for previous history. Pa_FlushStream + added by Augustus Saunders for Solaris compatibility. + PLB20021018 - Fill device info table with actual sample rates instead of wished for rates. + - Allow stream to open if sample rate within 10% of desired rate. + 20030630 - Thomas Richter - eliminated unused variable warnings. +*/ + +#include "pa_unix.h" + +#ifdef __linux__ +#include +#else +#include /* JH20010905 */ +#endif + + +#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. + */ +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 /* AFMT_S16_NE */ + + +/********************************************************************* + * Try to open the named device. + * If it opens, try to set various rates and formats and fill in + * the device info structure. + */ +PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad ) +{ + int result = paHostError; + int tempDevHandle; + int numChannels, maxNumChannels; + int format; + int numSampleRates; + int sampleRate; + int numRatesToTry; + int lastRate; + int ratesToTry[9] = {96000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}; + int i; + + /* douglas: + we have to do this querying in a slightly different order. apparently + some sound cards will give you different info based on their settings. + 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(("Pa_QueryDevice: could not open %s\n", deviceName )); + return paHostError; + } + + /* Ask OSS what formats are supported by the hardware. */ + pad->pad_Info.nativeSampleFormats = 0; + + if (ioctl(tempDevHandle, SNDCTL_DSP_GETFMTS, &format) == -1) + { + ERR_RPT(("Pa_QueryDevice: could not get format info\n" )); + goto error; + } + if( format & AFMT_U8 ) pad->pad_Info.nativeSampleFormats |= paUInt8; + if( format & AFMT_S16_NE ) pad->pad_Info.nativeSampleFormats |= paInt16; + + /* Negotiate for the maximum number of channels for this device. PLB20010927 + * Consider up to 16 as the upper number of channels. + * Variable numChannels 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(("Pa_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(("Pa_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(("Pa_QueryDevice: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", maxNumChannels )) + } + + pad->pad_Info.maxOutputChannels = maxNumChannels; + DBUG(("Pa_QueryDevice: maxNumChannels = %d\n", maxNumChannels)) + + /* 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); + } + + /* FIXME - for now, assume maxInputChannels = maxOutputChannels. + * Eventually do separate queries for O_WRONLY and O_RDONLY + */ + pad->pad_Info.maxInputChannels = pad->pad_Info.maxOutputChannels; + + DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", + pad->pad_Info.maxInputChannels)) + + + /* Determine available sample rates by trying each one and seeing result. + * OSS often supports funky rates such as 44188 instead of 44100! + */ + numSampleRates = 0; + lastRate = 0; + numRatesToTry = sizeof(ratesToTry)/sizeof(int); + for (i = 0; i < numRatesToTry; i++) + { + sampleRate = ratesToTry[i]; + + if (ioctl(tempDevHandle, SNDCTL_DSP_SPEED, &sampleRate) >= 0 ) /* PLB20010817 */ + { + /* Use whatever rate OSS tells us. PLB20021018 */ + if (sampleRate != lastRate) + { + DBUG(("Pa_QueryDevice: adding sample rate: %d\n", sampleRate)) + pad->pad_SampleRates[numSampleRates] = (float)sampleRate; + numSampleRates++; + lastRate = sampleRate; + } + else + { + DBUG(("Pa_QueryDevice: dang - got sample rate %d again!\n", sampleRate)) + } + } + } + + DBUG(("Pa_QueryDevice: final numSampleRates = %d\n", numSampleRates)) + if (numSampleRates==0) /* HP20010922 */ + { + /* Desparate attempt to keep running even though no good rates found! */ + ERR_RPT(("Pa_QueryDevice: no supported sample rate (or SNDCTL_DSP_SPEED ioctl call failed). Force 44100 Hz\n" )); + pad->pad_SampleRates[numSampleRates++] = 44100; + } + + pad->pad_Info.numSampleRates = numSampleRates; + pad->pad_Info.sampleRates = pad->pad_SampleRates; /* use pointer to embedded array */ + + pad->pad_Info.name = deviceName; + + result = paNoError; + +error: + /* We MUST close the handle here or we won't be able to reopen it later!!! */ + close(tempDevHandle); + + return result; +} + +/*******************************************************************************************/ +PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate ) +{ + PaError result = paNoError; + int tmp; + + /* Set format, channels, and rate in this order to keep OSS happy. */ + /* Set data format. FIXME - handle more native formats. */ + tmp = AFMT_S16_NE; + if( ioctl(devHandle,SNDCTL_DSP_SETFMT,&tmp) == -1) + { + ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SETFMT\n" )); + return paHostError; + } + if( tmp != AFMT_S16_NE ) + { + ERR_RPT(("Pa_SetupDeviceFormat: HW does not support AFMT_S16_NE\n" )); + return paHostError; + } + + + /* Set number of channels. */ + tmp = numChannels; + if (ioctl(devHandle, SNDCTL_DSP_CHANNELS, &numChannels) == -1) + { + ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_CHANNELS\n" )); + return paHostError; + } + if( tmp != numChannels) + { + ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d channels\n", numChannels )); + return paHostError; + } + + /* Set playing frequency. */ + tmp = sampleRate; + if( ioctl(devHandle,SNDCTL_DSP_SPEED,&tmp) == -1) + { + ERR_RPT(("Pa_SetupDeviceFormat: could not SNDCTL_DSP_SPEED\n" )); + return paHostError; + } + else if( tmp != sampleRate ) + { + int percentError = abs( (100 * (sampleRate - tmp)) / sampleRate ); + PRINT(("Pa_SetupDeviceFormat: warning - requested sample rate = %d Hz - closest = %d\n", + sampleRate, tmp )); + /* Allow sample rate within 10% off of requested rate. PLB20021018 + * Sometimes OSS uses a funky rate like 44188 instead of 44100. + */ + if( percentError > 10 ) + { + ERR_RPT(("Pa_SetupDeviceFormat: HW does not support %d Hz sample rate\n",sampleRate )); + return paHostError; + } + } + + return result; +} + +PaError Pa_SetupOutputDeviceFormat( int devHandle, int numChannels, int sampleRate ) +{ + return Pa_SetupDeviceFormat(devHandle, numChannels, sampleRate); +} + +PaError Pa_SetupInputDeviceFormat( int devHandle, int numChannels, int sampleRate ) +{ + return Pa_SetupDeviceFormat(devHandle, numChannels, sampleRate); +} + + +/******************************************************************************************* +** Set number of fragments and size of fragments to achieve desired latency. +*/ + +static int CalcHigherLogTwo( int n ) +{ + int log2 = 0; + while( (1< 8 ) + { + numBuffers = (numBuffers + 1) >> 1; + framesPerBuffer = framesPerBuffer << 1; + } + + /* calculate size of buffers in bytes */ + bufferSize = framesPerBuffer * channelsPerFrame * sizeof(short); /* FIXME - other sizes? */ + + /* Calculate next largest power of two */ + powerOfTwo = CalcHigherLogTwo( bufferSize ); + DBUG(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n", + numBuffers, framesPerBuffer, powerOfTwo )); + + /* Encode info into a single int */ + tmp=(numBuffers<<16) + powerOfTwo; + + if(ioctl(devHandle,SNDCTL_DSP_SETFRAGMENT,&tmp) == -1) + { + ERR_RPT(("Pa_SetLatency: could not SNDCTL_DSP_SETFRAGMENT\n" )); + /* Don't return an error. Best to just continue and hope for the best. */ + ERR_RPT(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d, powerOfTwo = %d\n", + numBuffers, framesPerBuffer, powerOfTwo )); + } +} + +/***********************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + internalPortAudioStream *past = (internalPortAudioStream *) stream; + PaHostSoundControl *pahsc; + + count_info info; + int delta; + + if( past == NULL ) return paBadStreamPtr; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + + if( pahsc->pahsc_NativeOutputBuffer ) + { + ioctl(pahsc->pahsc_OutputHandle, SNDCTL_DSP_GETOPTR, &info); + delta = (info.bytes - pahsc->pahsc_LastPosPtr) & 0x000FFFFF; + return (pahsc->pahsc_LastStreamBytes + delta) / (past->past_NumOutputChannels * sizeof(short)); + } + else + { + ioctl(pahsc->pahsc_InputHandle, SNDCTL_DSP_GETIPTR, &info); + delta = (info.bytes - pahsc->pahsc_LastPosPtr) & 0x000FFFFF; + return (pahsc->pahsc_LastStreamBytes + delta) / (past->past_NumInputChannels * sizeof(short)); + } +} + +void Pa_UpdateStreamTime(PaHostSoundControl *pahsc) +{ + count_info info; + int delta; + + /* Update current stream time (using a double so that + we don't wrap around like info.bytes does) */ + if( pahsc->pahsc_NativeOutputBuffer ) + { + ioctl(pahsc->pahsc_OutputHandle, SNDCTL_DSP_GETOPTR, &info); + } + else + { + ioctl(pahsc->pahsc_InputHandle, SNDCTL_DSP_GETIPTR, &info); + } + delta = (info.bytes - pahsc->pahsc_LastPosPtr) & 0x000FFFFF; + pahsc->pahsc_LastStreamBytes += delta; + pahsc->pahsc_LastPosPtr = info.bytes; +} + +PaError Pa_FlushStream(int devHandle) +{ + /* AS: This doesn't do anything under OSS; it was added for Solaris.*/ + devHandle = devHandle; /* unused */ + return paNoError; +} -- cgit v1.2.1