This commit is contained in:
2025-11-24 14:19:51 +05:30
commit f5c1412b28
6734 changed files with 1527575 additions and 0 deletions

View File

@ -0,0 +1,32 @@
# Make PortAudio for Linux
#
# by Phil Burk
#
# To compile a test program, make a target with a .app suffix.
# For example to compile "../pa_tests/pa_devs.c", enter:
# gmake pa_devs.app
#
# To compile and execute a test program, make a target with a .run suffix.
# For example to build and run "../pa_tests/pa_devs.c", enter:
# gmake pa_devs.run
VPATH = ../pa_common ../pa_tests
CFLAGS = -g -Wall -I../pa_common
CC = gcc
PAOBJS = pa_lib.o pa_unix_oss.o pa_unix.o
PAINC = portaudio.h pa_unix.h
PALIBS = -lm -lpthread
all: patest_sine.run
%.app: %.o $(PAOBJS) $(PAINC) Makefile
gcc $(CFLAGS) $*.o $(PAOBJS) $(PALIBS) -o $@
%.run: %.app
./$*.app
clean:
-rm *.app
-rm *.o

View File

@ -0,0 +1,36 @@
# Make PortAudio for FreeBSD
LIBS = -lm -pthread
CDEFINES = -I../pa_common
CFLAGS = -g
PASRC = ../pa_common/pa_lib.c pa_freebsd.c
PAINC = ../pa_common/portaudio.h
# Tests that work.
#TESTC = $(PASRC) ../pa_tests/patest_sine.c
TESTC = $(PASRC) ../pa_tests/patest_sine_time.c
#TESTC = $(PASRC) ../pa_tests/patest_stop.c
#TESTC = $(PASRC) ../pa_tests/patest_sync.c
#TESTC = $(PASRC) ../pa_tests/patest_pink.c
#TESTC = $(PASRC) ../pa_tests/patest_leftright.c
#TESTC = $(PASRC) ../pa_tests/patest_clip.c
#TESTC = $(PASRC) ../pa_tests/patest_dither.c
#TESTC = $(PASRC) ../pa_tests/pa_devs.c
#TESTC = $(PASRC) ../pa_tests/patest_many.c
#TESTC = $(PASRC) ../pa_tests/patest_record.c
#TESTC = $(PASRC) ../pa_tests/patest_wire.c
#TESTC = $(PASRC) ../pa_tests/paqa_devs.c
# Tests that do not yet work.
TESTH = $(PAINC)
all: patest
patest: $(TESTC) $(TESTH) Makefile
gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
run: patest
./patest

View File

@ -0,0 +1,94 @@
From: "Benno Senoner" <sbenno@gardena.net>
To: <music-dsp@shoko.calarts.edu>
Subject: Re: [music-dsp] coding realtime guitar efx on a "pc"
Date: Saturday, June 30, 2001 8:19 AM
Andr<EFBFBD>,
you are solving your problem the wrong way:
you need to use a single threaded solution which does this:
- set the audio I/O parameters to fragnum=4 fragsize=128 bytes (=32samples) if
you use stereo or fragsize=64 bytes (=32 samples) if you use mono.
(do not forget to activate fulltuplex with using the _TRIGGER_ stuff)
(you need to frist deactivate audio and then start the trigger after the DAC is
prefilled (see below))
This will give you a total input to output latency of 4x32 samples
= 128 samples which at 44.1kHz correspond to 2.9msec latency.
now set your process to SCHED_FIFO (see man sched_setscheduler)
after the initialization your code should do more than less this:
- write() 4 x 32 samples to the audio fd in order to prefill the DAC.
Without this you will get dropouts.
while(1) {
read() 32 samples from ADC
perform_dsp_stuff() on the 32 samples
write() 32 samples to DAC
}
If you use a low latency kernel and pay attention to all the stuff above, then
you will get rock solid 3msec latencies (plus eventual converter latencies but
these are in the 1-2msec range AFAIK).
Using multiple threads , pipes etc, only complicates your life and often makes
it impossible to achieve these low latences.
Realtime/audio programming is not an easy task , this is why people often
fail to get the desired results even if their hardware is low-latency capable.
The problem is that the final latency depends on the hardware you use,
the application and the operating system.
cheers,
Benno.
http://www.linuxaudiodev.org The Home of Linux Audio Development
On Sat, 30 Jun 2001, you wrote:
> On 2001-06-29 21:38 +0200, Benno Senoner wrote:
>
> > OSS/Free refuses to use a low # of frags ?
> >
> > That's a myth.
>
> I hope it is. :-)
>
> The fact is that ioctl(SNDCTL_DSP_SETFRAGMENT) succeeds with
> values as low a 0x10007 (one 128-B fragment) but the latency is
> still high enough to be clearly noticeable, which suggests that
> it's *way* above 2/3 ms. This is on an otherwise idle machine
> equipped with a SB PCI 128.
>
> But maybe it's me who's doing something wrong. I've been careful
> to flush stdio buffers or use unbuffered I/O (write(2)) but I
> may have let something else through.
>
> For example, since the signal processing and the I/O are done by
> two different vanilla processes communicating via pipes, it may
> be a scheduling granularity problem (E.G. the kernel giving the
> I/O process a time slice every 20 ms).
>
> --
> Andr<64> Majorel <amajorel@teaser.fr>
> http://www.teaser.fr/~amajorel/
>
> dupswapdrop -- the music-dsp mailing list and website: subscription info,
> FAQ, source code archive, list archive, book reviews, dsp links
> http://shoko.calarts.edu/musicdsp/
--
dupswapdrop -- the music-dsp mailing list and website: subscription info,
FAQ, source code archive, list archive, book reviews, dsp links
http://shoko.calarts.edu/musicdsp/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
/*
* 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. */
/*
PROPOSED - should we add this to "portaudio.h". Problem with
Pa_QueryDevice() not having same driver name os Pa_OpenStream().
A PaDriverInfo structure can be passed to the underlying device
on the Pa_OpenStream() call. The contents and interpretation of
the structure is determined by the PA implementation.
*/
typedef struct PaDriverInfo /* PROPOSED */
{
/* Size of structure. Allows driver to extend the structure without breaking existing applications. */
int size;
/* Can be used to request a specific device name. */
const char *name;
unsigned long data;
}
PaDriverInfo;
#include <stdio.h>
#include <stdlib.h>
//#include <malloc.h>
#include <memory.h>
#include <math.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <sched.h>
#include <pthread.h>
#include <errno.h>
#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"
#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x) /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) */
#define BAD_DEVICE_ID (-1)
#define MIN_LATENCY_MSEC (100)
#define MIN_TIMEOUT_MSEC (100)
#define MAX_TIMEOUT_MSEC (1000)
/************************************************* Definitions ********/
#ifdef __linux__
#define DEVICE_NAME_BASE "/dev/dsp"
#else
#define DEVICE_NAME_BASE "/dev/audio"
#endif
#define MAX_CHARS_DEVNAME (32)
#define MAX_SAMPLE_RATES (10)
typedef struct internalPortAudioDevice
{
struct internalPortAudioDevice *pad_Next; /* Singly linked list. */
double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
char pad_DeviceName[MAX_CHARS_DEVNAME];
PaDeviceInfo pad_Info;
}
internalPortAudioDevice;
/* Define structure to contain all OSS and Linux specific data. */
typedef struct PaHostSoundControl
{
int pahsc_OutputHandle;
int pahsc_InputHandle;
int pahsc_AudioPriority; /* priority of background audio thread */
pthread_t pahsc_AudioThread; /* background audio thread */
int pahsc_IsAudioThreadValid; /* Is pahsc_AudioThread valid?*/ pid_t pahsc_AudioThreadPID; /* background audio thread */
pthread_t pahsc_WatchDogThread; /* highest priority thread that protects system */
int pahsc_IsWatchDogThreadValid; /* Is pahsc_WatchDogThread valid?*/
int pahsc_WatchDogRun; /* Ask WatchDog to stop. */
pthread_t pahsc_CanaryThread; /* low priority thread that detects abuse by audio */
int pahsc_IsCanaryThreadValid; /* Is pahsc_CanaryThread valid?*/
struct timeval pahsc_CanaryTime;
int pahsc_CanaryRun; /* Ask Canary to stop. */
short *pahsc_NativeInputBuffer;
short *pahsc_NativeOutputBuffer;
unsigned int pahsc_BytesPerInputBuffer; /* native buffer size in bytes */
unsigned int pahsc_BytesPerOutputBuffer; /* native buffer size in bytes */
/* For measuring CPU utilization. */
struct timeval pahsc_EntryTime;
double pahsc_InverseMicrosPerBuffer; /* 1/Microseconds of real-time audio per user buffer. */
/* For calculating stream time */
int pahsc_LastPosPtr;
double pahsc_LastStreamBytes;
}
PaHostSoundControl;
/************************************************* Prototypes **********/
internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id );
PaError Pa_QueryDevices( void );
PaError Pa_QueryDevice( const char *deviceName, internalPortAudioDevice *pad );
PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate );
PaError Pa_SetupInputDeviceFormat( int devHandle, int numChannels, int sampleRate );
PaError Pa_SetupOutputDeviceFormat( int devHandle, int numChannels, int sampleRate );
void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame );
void Pa_UpdateStreamTime(PaHostSoundControl *pahsc);
int Pa_FlushStream(int devHandle);

View File

@ -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 <linux/soundcard.h>
#else
#include <machine/soundcard.h> /* 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<<log2) < n ) log2++;
return log2;
}
void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame )
{
int tmp;
int bufferSize, powerOfTwo;
/* Increase size of buffers and reduce number of buffers to reduce latency inside driver. */
while( numBuffers > 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;
}

View File

@ -0,0 +1,397 @@
/*
* 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: Initial cut at Solaris modifications jointly by Sam Bayer
and Augustus Saunders.
20030206 - Martin Rohrbach - various mods for Solaris
*/
#define __solaris_native__
#include "pa_unix.h"
/* SAM 6/2/02: Docs say we should include sys/audio.h, but
that doesn't exist pre Solaris 2.8. These headers work fine. */
#include <sys/audioio.h>
#include <sys/stropts.h>
/*********************************************************************
* 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 numSampleRates;
int sampleRate;
int numRatesToTry;
int ratesToTry[9] = {96000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000};
int i;
audio_info_t solaris_info;
audio_device_t device_info;
/* 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
*/
/*
to check a device for it's capabilities, it's probably better to use the
equivalent "-ctl"-descriptor - MR
*/
char devname[strlen(deviceName) + 4];
if ( (tempDevHandle = open(strcat(strcpy(devname, deviceName), "ctl"), 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;
AUDIO_INITINFO(&solaris_info);
/* SAM 12/31/01: Sparc native does mulaw, alaw and PCM.
I think PCM is signed. */
for (i = 8; i <= 32; i += 8) {
solaris_info.play.precision = i;
solaris_info.play.encoding = AUDIO_ENCODING_LINEAR;
/* If there are no errors, add the format. */
if (ioctl(tempDevHandle, AUDIO_SETINFO, &solaris_info) > -1) {
switch (i) {
case 8:
pad->pad_Info.nativeSampleFormats |= paInt8;
break;
case 16:
pad->pad_Info.nativeSampleFormats |= paInt16;
break;
case 24:
pad->pad_Info.nativeSampleFormats |= paInt24;
break;
case 32:
pad->pad_Info.nativeSampleFormats |= paInt32;
break;
}
}
}
maxNumChannels = 0;
for( numChannels = 1; numChannels <= 16; numChannels++ )
{
int temp = numChannels;
DBUG(("Pa_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels ))
AUDIO_INITINFO(&solaris_info);
solaris_info.play.channels = temp;
if (ioctl(tempDevHandle, AUDIO_SETINFO, &solaris_info) < 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. */
}
}
pad->pad_Info.maxOutputChannels = maxNumChannels;
DBUG(("Pa_QueryDevice: maxNumChannels = %d\n", maxNumChannels))
/* 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.
*/
numSampleRates = 0;
AUDIO_INITINFO(&solaris_info);
numRatesToTry = sizeof(ratesToTry)/sizeof(int);
for (i = 0; i < numRatesToTry; i++)
{
sampleRate = ratesToTry[i];
solaris_info.play.sample_rate = sampleRate; /* AS: We opened for Write, so set play */
if (ioctl(tempDevHandle, AUDIO_SETINFO, &solaris_info) >= 0 ) /* PLB20010817 */
{
if (sampleRate == ratesToTry[i])
{
DBUG(("Pa_QueryDevice: got sample rate: %d\n", sampleRate))
pad->pad_SampleRates[numSampleRates] = (float)ratesToTry[i];
numSampleRates++;
}
}
}
DBUG(("Pa_QueryDevice: final numSampleRates = %d\n", numSampleRates))
if (numSampleRates==0) /* HP20010922 */
{
ERR_RPT(("Pa_QueryDevice: no supported sample rate (or SNDCTL_DSP_SPEED ioctl call failed).\n" ));
goto error;
}
pad->pad_Info.numSampleRates = numSampleRates;
pad->pad_Info.sampleRates = pad->pad_SampleRates;
/* query for the device name instead of using the filesystem-device - MR */
if (ioctl(tempDevHandle, AUDIO_GETDEV, &device_info) == -1) {
pad->pad_Info.name = deviceName;
} else {
char *pt = (char *)PaHost_AllocateFastMemory(strlen(device_info.name));
strcpy(pt, device_info.name);
pad->pad_Info.name = pt;
}
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_SetupInputDeviceFormat( int devHandle, int numChannels, int sampleRate )
{
audio_info_t solaris_info;
AUDIO_INITINFO(&solaris_info);
/* Sam Bayer/Bryan George 1/10/02: Various folks have
reported that on Solaris Ultra II, the not-right thing
happens on read unless you make sure the audio device is
flushed. The folks who wrote the Robust Audio Tool say:
+ XXX driver issue - on Ultra II's if you don't drain
* the device before reading commences then the device
* reads in blocks of 500ms irrespective of the
* blocksize set. After a minute or so it flips into the
* correct mode, but obviously this is too late to be + * useful for most apps. grrr.
*/
/* AS: And the Solaris man audio pages say you should flush before changing formats
anyway. So there you go. */
if (Pa_FlushStream(devHandle) != paNoError)
return paHostError;
solaris_info.record.encoding = AUDIO_ENCODING_LINEAR;
solaris_info.record.sample_rate = sampleRate;
solaris_info.record.precision = 16;
solaris_info.record.channels = numChannels;
if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
{
ERR_RPT(("Pa_SetupDeviceFormat: could not set audio info\n" ));
return paHostError;
}
return paNoError;
}
PaError Pa_SetupOutputDeviceFormat( int devHandle, int numChannels, int sampleRate )
{
audio_info_t solaris_info;
AUDIO_INITINFO(&solaris_info);
/* Sam Bayer/Bryan George 1/10/02: Various folks have
reported that on Solaris Ultra II, the not-right thing
happens on read unless you make sure the audio device is
flushed. The folks who wrote the Robust Audio Tool say:
+ XXX driver issue - on Ultra II's if you don't drain
* the device before reading commences then the device
* reads in blocks of 500ms irrespective of the
* blocksize set. After a minute or so it flips into the
* correct mode, but obviously this is too late to be + * useful for most apps. grrr.
*/
/* AS: And the Solaris man audio pages say you should flush before changing formats
anyway. So there you go. */
if (Pa_FlushStream(devHandle) != paNoError)
return paHostError;
solaris_info.play.encoding = AUDIO_ENCODING_LINEAR;
solaris_info.play.sample_rate = sampleRate;
solaris_info.play.precision = 16;
solaris_info.play.channels = numChannels;
if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
{
ERR_RPT(("Pa_SetupDeviceFormat: could not set audio info\n" ));
return paHostError;
}
return paNoError;
}
PaError Pa_SetupDeviceFormat( int devHandle, int numChannels, int sampleRate )
{
PaError result = paNoError;
result = Pa_SetupOutputDeviceFormat(devHandle, numChannels, sampleRate);
if (result != paNoError)
return result;
return Pa_SetupInputDeviceFormat(devHandle, numChannels, sampleRate);
}
/*******************************************************************************************
** Set number of fragments and size of fragments to achieve desired latency.
*/
static PaError Pa_Unpause(int devHandle);
static PaError Pa_PauseAndFlush(int devHandle);
void Pa_SetLatency( int devHandle, int numBuffers, int framesPerBuffer, int channelsPerFrame )
{
int bufferSize;
audio_info_t solaris_info;
/* Increase size of buffers and reduce number of buffers to reduce latency inside driver. */
while( numBuffers > 8 )
{
numBuffers = (numBuffers + 1) >> 1;
framesPerBuffer = framesPerBuffer << 1;
}
/* calculate size of buffers in bytes */
bufferSize = framesPerBuffer * channelsPerFrame * sizeof(short); /* FIXME - other sizes? */
DBUG(("Pa_SetLatency: numBuffers = %d, framesPerBuffer = %d\n",
numBuffers, framesPerBuffer));
/* SAM 6/6/02: Documentation says to pause and flush before
changing buffer size. */
if (Pa_PauseAndFlush(devHandle) != paNoError) {
ERR_RPT(("Pa_SetLatency: could not pause audio\n" ));
return;
}
AUDIO_INITINFO(&solaris_info);
/* AS: Doesn't look like solaris has multiple buffers,
so I'm being conservative and
making one buffer. Might not be what we want... */
solaris_info.play.buffer_size = solaris_info.record.buffer_size = bufferSize;
if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
{
ERR_RPT(("Pa_SetLatency: could not set audio info\n" ));
}
Pa_Unpause(devHandle);
}
/***********************************************************************/
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
{
internalPortAudioStream *past = (internalPortAudioStream *) stream;
PaHostSoundControl *pahsc;
audio_info_t solaris_info;
if( past == NULL ) return paBadStreamPtr;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
ioctl(pahsc->pahsc_OutputHandle, AUDIO_GETINFO, &solaris_info);
return solaris_info.play.samples;
}
void Pa_UpdateStreamTime(PaHostSoundControl *pahsc)
{
/* AS: Don't need to do anytying for this under Solaris.*/
}
static PaError Pa_PauseAndFlush(int devHandle)
{
audio_info_t solaris_info;
AUDIO_INITINFO(&solaris_info);
solaris_info.play.pause = solaris_info.record.pause = 1;
if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
{
ERR_RPT(("Pa_FlushStream failed.\n"));
return paHostError;
}
if (ioctl(devHandle, I_FLUSH, FLUSHRW) == -1)
{
ERR_RPT(("Pa_FlushStream failed.\n"));
/* Unpause! */
AUDIO_INITINFO(&solaris_info);
solaris_info.play.pause = solaris_info.record.pause = 0;
ioctl(devHandle, AUDIO_SETINFO, &solaris_info);
return paHostError;
}
return paNoError;
}
static PaError Pa_Unpause(int devHandle)
{
audio_info_t solaris_info;
AUDIO_INITINFO(&solaris_info);
solaris_info.play.pause = solaris_info.record.pause = 0;
if (ioctl(devHandle, AUDIO_SETINFO, &solaris_info) == -1)
{
ERR_RPT(("Pa_FlushStream failed.\n"));
return paHostError;
}
return paNoError;
}
PaError Pa_FlushStream(int devHandle)
{
PaError res = Pa_PauseAndFlush(devHandle);
if (res == paNoError)
return Pa_Unpause(devHandle);
else return res;
}

View File

@ -0,0 +1,114 @@
/*
* recplay.c
* Phil Burk
* Minimal record and playback test.
*
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#ifndef __STDC__
/* #include <getopt.h> */
#endif /* __STDC__ */
#include <fcntl.h>
#ifdef __STDC__
#include <string.h>
#else /* __STDC__ */
#include <strings.h>
#endif /* __STDC__ */
#include <sys/soundcard.h>
#define NUM_BYTES (64*1024)
#define BLOCK_SIZE (4*1024)
#define AUDIO "/dev/dsp"
char buffer[NUM_BYTES];
int audioDev = 0;
main (int argc, char *argv[])
{
int numLeft;
char *ptr;
int num;
int samplesize;
/********** RECORD ********************/
/* Open audio device. */
audioDev = open (AUDIO, O_RDONLY, 0);
if (audioDev == -1)
{
perror (AUDIO);
exit (-1);
}
/* Set to 16 bit samples. */
samplesize = 16;
ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
if (samplesize != 16)
{
perror("Unable to set the sample size.");
exit(-1);
}
/* Record in blocks */
printf("Begin recording.\n");
numLeft = NUM_BYTES;
ptr = buffer;
while( numLeft >= BLOCK_SIZE )
{
if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 )
{
perror (AUDIO);
exit (-1);
}
else
{
printf("Read %d bytes\n", num);
ptr += num;
numLeft -= num;
}
}
close( audioDev );
/********** PLAYBACK ********************/
/* Open audio device for writing. */
audioDev = open (AUDIO, O_WRONLY, 0);
if (audioDev == -1)
{
perror (AUDIO);
exit (-1);
}
/* Set to 16 bit samples. */
samplesize = 16;
ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize);
if (samplesize != 16)
{
perror("Unable to set the sample size.");
exit(-1);
}
/* Play in blocks */
printf("Begin playing.\n");
numLeft = NUM_BYTES;
ptr = buffer;
while( numLeft >= BLOCK_SIZE )
{
if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 )
{
perror (AUDIO);
exit (-1);
}
else
{
printf("Wrote %d bytes\n", num);
ptr += num;
numLeft -= num;
}
}
close( audioDev );
}