Initial revision

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@14212 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Adam Fedor 2002-07-30 17:01:47 +00:00
parent 61cd8f4f97
commit 72f3efecea
121 changed files with 30428 additions and 0 deletions

View file

@ -0,0 +1,65 @@
Portable header file to contain:
/*
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.audiomulch.com/portaudio/
*
* Copyright (c) 1999-2000 Ross Bencina and 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.
*
*/
Implementation files to contain:
/*
* PortAudio Portable Real-Time Audio Library
* Latest version at: http://www.audiomulch.com/portaudio/
* <platform> Implementation
* Copyright (c) 1999-2000 <author(s)>
*
* 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.
*
*/

View file

@ -0,0 +1,60 @@
# Make PortAudio for Linux
# Updated 2001/08/25 Bill Eldridge bill@rfa.org
# Updated 2001/10/16, philburk@softsynth.com, s/unix_oss/unix_oss/
# Updated 2002/04/30 Bill Eldridge bill@rfa.org
# Made the libinstall and tests compile a bit cleaner
# A pretty bare makefile, that figures out all the test files
# and compiles them against the library in the pa_unix_oss directory.
# Do "make all" and then when happy, "make libinstall"
# (if not happy, "make clean")
# The ldconfig stuff in libinstall is the wrong way to do it -
# someone tell me the right way, please
LIBS = -lm -lpthread
CC = /usr/local/bin/gcc
CDEFINES = -I../pa_common
CFLAGS = -g
LIBINST = /usr/local/lib
TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c)
TESTO:= $(wildcard pa_tests/pa*.o pa_tests/debug*.o)
LIBFILES:= ./pa_common/pa_lib.c ./pa_unix_oss/pa_unix_oss.c
#all: sharedlib libinstall tests
all: sharedlib libinstall testo testq
.c.o:
-gcc -c -I./pa_common $< -o $*.o
.o:
-gcc $*.o -o $* -Lpa_unix_oss $(LIBS) -lportaudio
#.c.o:
# -gcc -c -I./pa_common $< -o $*.o
# -gcc $*.o -o $* -Lpa_unix_oss $(LIBS) -lportaudio
sharedlib: $(LIBFILES:.c=.o)
gcc -shared -o ./pa_unix_oss/libportaudio.so ./pa_common/pa_lib.o ./pa_unix_oss/pa_unix_oss.o
libinstall: ./pa_unix_oss/libportaudio.so
@cp -f ./pa_unix_oss/libportaudio.so $(LIBINST)
@/sbin/ldconfig
testo: $(TESTS:.c=.o)
testq: $(TESTO:.o=)
clean:
-@rm -f $(TESTS:.c=.o)
-@rm -f $(TESTS:.c=)
-@rm -f $(LIBFILES:.c=.o)
-@rm -f ./pa_unix_oss/libportaudio.so

View file

@ -0,0 +1,57 @@
# Makefile for PortAudio on mingw (http://mingw.sourceforge.net)
# Contributed by Bill Eldridge, bill@rfa.org, Radio Free Asia
# Copyright 2002/02/20, GPL
# Uses a common mingw32 cross-compiler that defaults
# to everything in /usr/local/cross-tools
# First edit your path with
# export PATH=/usr/local/cross-tools/bin:$PATH
# Usage: make -f Makefile.mingw all
# or make -f Makefile.mingw sharedlib
# make -f Makefile.mingw tests
#
# Then copy executables & portaudio.dll to your Windows machine
#
# To make work with pa_win_ds, you'll have to substitue
# all the pa_win_wmme files with pa_win_ds files, no biggie.
CC= i586-mingw32msvc-gcc
DLLTOOL= i586-mingw32msvc-dlltool
DLLWRAP= i586-mingw32msvc-dllwrap
ARCH= pa_win_wmme
TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c)
.c.o:
-$(CC) -c -I./pa_common $< -o $*.o
-$(CC) $*.o -o $*.exe -L/usr/local/lib -L$(ARCH) -lportaudio.dll -lwinmm
all: sharedlib tests
sharedlib: ./pa_common/pa_lib.c
$(CC) -c -I./pa_common pa_common/pa_lib.c -o pa_common/pa_lib.o
$(CC) -c -I./pa_common pa_win_wmme/pa_win_wmme.c -o pa_win_wmme/pa_win_wmme.o
$(CC) -shared -mthreads -o portaudio.dll pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/local/cross-tools/i586-win32msvc/lib -lwinmm -lm
$(DLLWRAP) --export-all --output-def=libportaudio.def --output-lib=libportaudio.a --dllname=portaudio.dll --drivername=i586-mingw32msvc-gcc pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/local/cross-tools/i586-win32msvc/lib -lwinmm -lm
$(CC) -shared -Wl,--enable-auto-image-base -o portaudio.dll -Wl,--out-implib=pa_win_wmme/libportaudio.dll.a pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/local/cross-tools/i586-win32msvc/lib -lwinmm
tests: $(TESTS:.c=.o)
sine:
$(CC) -c -I./pa_common pa_tests/patest_sine.c -o pa_tests/patest_sine.o
$(CC) pa_tests/patest_sine.o -o pa_tests/patest_sine.exe -L/usr/local/lib -lportaudio.dll -lwinmm
clean:
-rm ./pa_tests/*.exe
-rm ./pa_tests/*.o
nothing:
$(CC) pa_tests/patest_sine.o -L/usr/lib/w32api -L./pa_win_wmme -lportaudio.dll -lwinmm

View file

@ -0,0 +1,81 @@
README for PortAudio
Implementations for PC DirectSound and Mac SoundManager
/*
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com//
*
* Copyright (c) 1999-2000 Phil Burk and Ross Bencina
*
* 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.
*
*/
PortAudio is a portable audio I/O library designed for cross-platform
support of audio. It uses a callback mechanism to request audio processing.
Audio can be generated in various formats, including 32 bit floating point,
and will be converted to the native format internally.
Documentation:
See "pa_common/portaudio.h" for API spec.
See docs folder for a tutorial.
Also see http://www.portaudio.com/docs/
And see "pa_tests/patest_saw.c" for an example.
For information on compiling programs with PortAudio, please see the
tutorial at:
http://www.portaudio.com/docs/pa_tutorial.html
Important Files and Folders:
pa_common/ = platform independant code
pa_common/portaudio.h = header file for PortAudio API. Specifies API.
pa_common/pa_lib.c = host independant code for all implementations.
pablio = simple blocking read/write interface
Platform Implementations
pa_asio = ASIO for Windows and Macintosh
pa_beos = BeOS
pa_mac = Macintosh Sound Manager for OS 8,9 and Carbon
pa_mac_core = Macintosh Core Audio for OS X
pa_sgi = Silicon Graphics AL
pa_unix_oss = OSS implementation for various Unixes
pa_win_ds = Windows Direct Sound
pa_win_wmme = Windows MME (most widely supported)
Test Programs
pa_tests/pa_fuzz.c = guitar fuzz box
pa_tests/pa_devs.c = print a list of available devices
pa_tests/pa_minlat.c = determine minimum latency for your machine
pa_tests/paqa_devs.c = self test that opens all devices
pa_tests/paqa_errs.c = test error detection and reporting
pa_tests/patest_clip.c = hear a sine wave clipped and unclipped
pa_tests/patest_dither.c = hear effects of dithering (extremely subtle)
pa_tests/patest_pink.c = fun with pink noise
pa_tests/patest_record.c = record and playback some audio
pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad().
pa_tests/patest_sine.c = output a sine wave in a simple PA app
pa_tests/patest_sync.c = test syncronization of audio and video
pa_tests/patest_wire.c = pass input to output, wire simulator

View file

@ -0,0 +1,60 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="PortAudio Docs, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Docs</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Documentation</h1></center>
</td>
</tr>
</table></center>
<p>Copyright 2000 Phil Burk and Ross Bencina
<br>&nbsp;
<h3>
<a href="portaudio_h.txt">API Reference</a></h3>
<blockquote>The Application Programmer Interface is documented in "portaudio.h".</blockquote>
<h3>
<a href="pa_tutorial.html">Tutorial</a></h3>
<blockquote>Describes how to write audio programs using the PortAudio API.</blockquote>
<h3>
<a href="pa_impl_guide.html">Implementation Guide</a></h3>
<blockquote>Describes how to write an implementation of PortAudio for a
new computer platform.</blockquote>
<h3>
<a href="portaudio_icmc2001.pdf">Paper Presented at ICMC2001</a> (PDF)</h3>
<blockquote>Describes the PortAudio API and discusses implementation issues.
Written July 2001.</blockquote>
<h3>
<a href="latency.html">Improving Latency</a></h3>
<blockquote>How to tune your computer to achieve the lowest possible audio
delay.</blockquote>
<h3>
<a href="proposals.html">Proposed Changes</a></h3>
<blockquote>Describes API changes being considered by the developer community.
Feedback welcome.</blockquote>
<a href="http://www.portaudio.com/">Return to PortAudio Home Page</a>
</body>
</html>

View file

@ -0,0 +1,192 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Internal docs. How a stream is started or stopped.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Implementation - Start/Stop</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
<a href="http://www.portaudio.com">PortAudio</a> Latency</h1></center>
</td>
</tr>
</table></center>
<p>This page discusses the issues of audio latency for <a href="http://www.portaudio.com">PortAudio</a>
. It offers suggestions on how to lower latency to improve the responsiveness
of applications.
<blockquote><b><a href="#what">What is Latency?</a></b>
<br><b><a href="#portaudio">PortAudio and Latency</a></b>
<br><b><a href="#macintosh">Macintosh</a></b>
<br><b><a href="#unix">Unix</a></b>
<br><b><a href="#windows">WIndows</a></b></blockquote>
By Phil Burk, Copyright 2002 Phil Burk and Ross Bencina
<h2>
<a NAME="what"></a>What is Latency?</h2>
Latency is basically longest time that you have to wait before you obtain
a desired result. For digital audio output it is the time between making
a sound in software and finally hearing it.
<p>Consider the example of pressing a key on the ASCII keyboard to play
a note. There are several stages in this process which each contribute
their own latency. First the operating system must respond to the keypress.
Then the audio signal generated must work its way through the PortAudio
buffers. Then it must work its way through the audio card hardware. Then
it must go through the audio amplifier which is very quick and then travel
through the air. Sound travels at abous one foot per millisecond through
air so placing speakers across the room can add 5-20 msec of delay.
<p>The reverse process occurs when recording or responding to audio input.
If you are processing audio, for example if you implement a software guitar
fuzz box, then you have both the audio input and audio output latencies
added together.
<p>The audio buffers are used to prevent glitches in the audio stream.
The user software writes audio into the output buffers. That audio is read
by the low level audio driver or by DMA and sent to the DAC. If the computer
gets busy doing something like reading the disk or redrawing the screen,
then it may not have time to fill the audio buffer. The audio hardware
then runs out of audio data, which causes a glitch. By using a large enough
buffer we can ensure that there is always enough audio data for the audio
hardware to play. But if the buffer is too large then the latency is high
and the system feels sluggish. If you play notes on the keyboard then the
"instrument" will feel unresponsive. So you want the buffers to be as small
as possible without glitching.
<h2>
<a NAME="portaudio"></a>PortAudio and Latency</h2>
The only delay that PortAudio can control is the total length of its buffers.
The Pa_OpenStream() call takes two parameters: numBuffers and framesPerBuffer.
The latency is also affected by the sample rate which we will call framesPerSecond.
A frame is a set of samples that occur simultaneously. For a stereo stream,
a frame is two samples.
<p>The latency in milliseconds due to this buffering&nbsp; is:
<blockquote><tt>latency_msec = 1000 * numBuffers * framesPerBuffer / framesPerSecond</tt></blockquote>
This is not the total latency, as we have seen, but it is the part we can
control.
<p>If you call Pa_OpenStream() with numBuffers equal to zero, then PortAudio
will select a conservative number that will prevent audio glitches. If
you still get glitches, then you can pass a larger value for numBuffers
until the glitching stops. if you try to pass a numBuffers value that is
too small, then PortAudio will use its own idea of the minimum value.
<p>PortAudio decides on the minimum number of buffers in a conservative
way based on the frameRate, operating system and other variables. You can
query the value that PortAudio will use by calling:
<blockquote><tt>int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate
);</tt></blockquote>
On some systems you can override the PortAudio minimum if you know your
system can handle a lower value. You do this by setting an environment
variable called PA_MIN_LATENCY_MSEC which is read by PortAudio when it
starts up. This is supported on the PortAudio implementations for Windows
MME, Windows DirectSound, and Unix OSS.
<h2>
<a NAME="macintosh"></a>Macintosh</h2>
The best thing you can do to improve latency on Mac OS 8 and 9 is to turn
off Virtual Memory. PortAudio V18 will detect that Virtual Memory is turned
off and use a very low latency.
<p>For Mac OS X the latency is very low because Apple Core Audio is so
well written. You can set the PA_MIN_LATENCY_MSEC variable using:
<blockquote><tt>setenv PA_MIN_LATENCY_MSEC 4</tt></blockquote>
<h2>
<a NAME="unix"></a>Unix</h2>
PortAudio under Unix currently uses a backgroud thread that reads and writes
to OSS. This gives you decent but not great latency. But if you raise the
priority of the background thread to a very priority then you can get under
10 milliseconds latency. In order to raise your priority you must run the
PortAudio program as root! You must also set PA_MIN_LATENCY_MSEC using
the appropriate command for your shell.
<h2>
<a NAME="windows"></a>Windows</h2>
Latency under Windows is a complex issue because of all the alternative
operating system versions and device drivers. I have seen latency range
from 8 milliseconds to 400 milliseconds. The worst case is when using Windows
NT. Windows 98 is a little better, and Windows XP can be quite good if
properly tuned.
<p>The underlying audio API also makes a lot of difference. If the audio
device has its own DirectSound driver then DirectSound can often provide
better latency than WMME. But if a real DirectSound driver is not available
for your device then it is emulated using WMME and the latency can be very
high. That's where I saw the 400 millisecond latency. The ASIO implementation
is generally very good and will give the lowest latency if available.
<p>You can set the PA_MIN_LATENCY_MSEC variable to 50, for example, by
entering in MS-DOS:
<blockquote><tt>set PA_MIN_LATENCY_MSEC=50</tt></blockquote>
If you enter this in a DOS window then you must run the PortAudio program
from that same window for the variable to have an effect. You can add that
line to your C:\AUTOEXEC.BAT file and reboot if you want it to affect any
PortAudio based program.
<p>For Windows XP, you can set environment variables as follows:
<ol>
<li>
Select "Control Panel" from the "Start Menu".</li>
<li>
Launch the "System" Control Panel</li>
<li>
Click on the "Advanced" tab.</li>
<li>
Click on the "Environment Variables" button.</li>
<li>
Click "New" button under&nbsp; User Variables.</li>
<li>
Enter PA_MIN_LATENCY_MSEC for the name and some optimistic number for the
value.</li>
<li>
Click OK, OK, OK.</li>
</ol>
<h3>
Improving Latency on Windows</h3>
There are several steps you can take to improve latency under windows.
<ol>
<li>
Avoid reading or writng to disk when doing audio.</li>
<li>
Turn off all automated background tasks such as email clients, virus scanners,
backup programs, FTP servers, web servers, etc. when doing audio.</li>
<li>
Disconnect from the network to prevent network traffic from interrupting
your CPU.</li>
</ol>
<b>Important: </b>Windows XP users can also tune the OS to favor background
tasks, such as audio, over foreground tasks, such as word processing. I
lowered my latency from 40 to 10 milliseconds using this simple technique.
<ol>
<li>
Select "Control Panel" from the "Start Menu".</li>
<li>
Launch the "System" Control Panel</li>
<li>
Click on the "Advanced" tab.</li>
<li>
Click on the "Settings" button in the Performance area.</li>
<li>
Click on the "Advanced" tab.</li>
<li>
Select "Background services" in the Processor Scheduling area.</li>
<li>
Click OK, OK.</li>
</ol>
Please let us know if you have others sugestions for lowering latency.
<br>&nbsp;
<br>&nbsp;
</body>
</html>

View file

@ -0,0 +1,488 @@
/*
This file contains the host-neutral code for implementing multiple driver model
support in PortAudio.
It has not been compiled, but it is supplied only for example purposes at this stage.
TODO: use of CHECK_DRIVER_MODEL is bogus in some instances since some
of those functions don't return a PaError
*/
#include "pa_drivermodel.h.txt"
#ifndef PA_MULTIDRIVER
/* single driver support, most functions will stay in the implementation files */
PaDriverModelID Pa_CountDriverModels()
{
return 1;
}
/*
Perhaps all drivers should define this with this signature
const PaDriverModelInfo* Pa_GetDriverModelInfo( PaDriverModelID driverModelID )
{
}
*/
PaDeviceID Pa_DriverModelDefaultInputDeviceID( PaDriverModelID driverModelID )
{
return Pa_GetDefaultInputDeviceID();
}
PaDeviceID Pa_DriverModelDefaultOutputDeviceID( PaDriverModelID driverModelID )
{
return Pa_GetDefaultInputDeviceID();
}
/*
Perhaps all drivers should define with this signature
int Pa_DriverModelMinNumBuffers( PaDriverModelID driverModelID, int framesPerBuffer, double sampleRate )
{
}
*/
int Pa_DriverModelCountDevices( PaDriverModelID driverModelID )
{
return Pa_CountDevices();
}
PaDeviceID Pa_DriverModelGetDeviceID(PaDriverModelID driverModelID, int perDriverModelIndex )
{
return perDriverModelIndex;
}
#else
/* multidriver support */
typedef PaError (*PaInitializeFunPtr)( PaDriverModelImplementation** );
/*
the initializers array is a static table of function pointers
to all the available driverModels on the current platform.
the order of pointers in the array is important. the first
pointer is always considered to be the "default" driver model.
*/
static PaInitializeFunPtr driverModelInitializers[] = {
#ifdef WINDOWS
PaWin32WMME_MultiDriverInitialize,
PaWin32DS_MultiDriverInitialize,
PaASIO_MultiDriverInitialize
#endif
#ifdef MAC
PaMacSM_MultiDriverInitialize,
PaMacCA_MultiDriverInitialize,
PaASIO_MultiDriverInitialize
#endif
/* other platforms here */
(PaInitializeFunPtr*)0 /* NULL terminate the list */
};
/*
the driverModels array is a dynamically created table of
currently available driverModels.
*/
static PaDriverModelImplementation* driverModels = 0;
static int numDriverModels = 0;
#define PA_CHECK_INITIALIZED\
if( driverModels == 0 )
return paLibraryNotInitialised
#define PA_CHECK_DRIVER_MODEL_ID( id )
if( id < 0 || id >= numDriverModels )
return paBadDriverModelID;
/*
ConvertPublicDeviceIdToImplementationDeviceId converts deviceId
from a public device id, to a device id for a particular
PortAudio implementation. On return impl will either point
to a valid implementation or will be NULL.
*/
static void ConvertPublicDeviceIDToImplementationDeviceID(
PaDriverModelImplementation *impl, PaDeviceID deviceID )
{
int i, count;
impl = NULL;
for( i=0; i < numDriverModels; ++i ){
count = driverModels[i]->countDevices();
if( deviceID < count ){
impl = driverModels[i];
return NULL;
}else{
deviceID -= count;
}
}
}
static PaDeviceID ConvertImplementationDeviceIDToPublicDeviceID(
PaDriverModelID driverModelID, PaDeviceID deviceID )
{
int i;
for( i=0; i < driverModelID; ++i )
deviceID += driverModels[i]->countDevices();
}
PaError Pa_Initialize( void )
{
PaError result = paNoError;
int i, initializerCount;
PaDriverModelImplementation *impl;
if( driverModels != 0 )
return paAlreadyInitialized;
/* count the number of driverModels */
initializerCount=0;
while( driverModelInitializers[initializerCount] != 0 ){
++initializerCount;
}
driverModels = malloc( sizeof(PaDriverModelImplementation*) * initializerCount );
if( driverModels == NULL )
return paInsufficientMemory;
numDriverModels = 0;
for( i=0; i<initializerCount; ++i ){
result = (*driverModelInitializers[i])( &impl );
if( result == paNoError ){
driverModels[numDriverModels] = impl;
++numDriverModels;
}else{
// TODO: terminate the drivers which have already been initialized.
}
}
return result;
}
PaError Pa_Terminate( void )
{
int i;
PA_CHECK_INITIALIZED;
/*
rather than require each implementation to do it separately we could
keep track of all open streams and close them here
*/
for( i=0; i<numDriverModels; ++i )
driverModels[i]->terminate( driverModels[i] );
}
long Pa_GetHostError( void )
{
PA_CHECK_INITIALIZED;
under construction. depends on error text proposal.
}
const char *Pa_GetErrorText( PaError errnum )
{
PA_CHECK_INITIALIZED;
under construction. may need to call driver model specific code
depending on how the error text proposal pans out.
}
int Pa_CountDevices()
{
int i, result;
PA_CHECK_INITIALIZED;
result = 0;
for( i=0; i < numDriverModels; ++i )
result += driverModels[i]->countDevices();
return result;
}
PaDeviceID Pa_GetDefaultInputDeviceID( void )
{
PA_CHECK_INITIALIZED;
return driverModels[0]->getDefaultInputDeviceID();
}
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
{
PA_CHECK_INITIALIZED;
return driverModels[0]->getDefaultInputDeviceID();
}
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID deviceID )
{
PaDriverModelImplementation *impl;
PA_CHECK_INITIALIZED;
ConvertPublicDeviceIDToImplementationDeviceID( impl, deviceID );
if( impl == NULL )
return paInvalidDeviceID;
return impl->getDeviceInfo( deviceID );
}
/* NEW MULTIPLE DRIVER MODEL FUNCTIONS ---------------------------------- */
PaDriverModelID Pa_CountDriverModels()
{
PA_CHECK_INITIALIZED;
return numDriverModels;
}
const PaDriverModelInfo* Pa_GetDriverModelInfo( PaDriverModelID driverModelID )
{
PA_CHECK_INITIALIZED;
PA_CHECK_DRIVER_MODEL_ID( driverModelID );
return driverModels[ driverModelID ]->getDriverModelInfo();
}
PaDeviceID Pa_DriverModelDefaultInputDeviceID( PaDriverModelID driverModelID )
{
PA_CHECK_INITIALIZED;
PA_CHECK_DRIVER_MODEL_ID( driverModelID );
return ConvertImplementationDeviceIDToPublicDeviceID( driverModelID,
driverModels[ driverModelID ]->getDefaultInputDeviceID();
}
PaDeviceID Pa_DriverModelDefaultOutputDeviceID( PaDriverModelID driverModelID )
{
PA_CHECK_INITIALIZED;
PA_CHECK_DRIVER_MODEL_ID( driverModelID );
return ConvertImplementationDeviceIDToPublicDeviceID( driverModelID,
driverModels[ driverModelID ]->getDefaultOutputDeviceID();
}
int Pa_DriverModelMinNumBuffers( PaDriverModelID driverModelID, int framesPerBuffer, double sampleRate )
{
PA_CHECK_INITIALIZED;
PA_CHECK_DRIVER_MODEL_ID( driverModelID );
return driverModels[ driverModelID ]->getMinNumBuffers( int framesPerBuffer, double sampleRate );
}
int Pa_DriverModelCountDevices( PaDriverModelID driverModelID )
{
PA_CHECK_INITIALIZED;
PA_CHECK_DRIVER_MODEL_ID( driverModelID );
return driverModels[ driverModelID ]->coundDevices();
}
PaDeviceID Pa_DriverModelGetDeviceID(PaDriverModelID driverModelID, int perDriverModelIndex )
{
PA_CHECK_INITIALIZED;
PA_CHECK_DRIVER_MODEL_ID( driverModelID );
return ConvertImplementationDeviceIDToPublicDeviceID( driverModelID, perDriverModelIndex );
}
/* END NEW MULTIPLE DRIVER MODEL FUNCTIONS ------------------------------ */
PaError Pa_OpenStream( PortAudioStream** stream,
PaDeviceID inputDevice,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDevice,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PaStreamFlags streamFlags,
PortAudioCallback *callback,
void *userData )
{
PaError result;
PaDriverModelImplementation *inputImpl, *outputImpl, impl;
PA_CHECK_INITIALIZED;
if( inputDevice != paNoDevice ){
ConvertPublicDeviceIDToImplementationDeviceID( inputImpl, inputDevice );
if( inputImpl == NULL )
return paInvalidDeviceID;
else
impl = inputImpl;
}
if( outputDevice != paNoDevice ){
ConvertPublicDeviceIDToImplementationDeviceID( outputImpl, outputDevice );
if( outputImpl == NULL )
return paInvalidDeviceID;
else
impl = outputImpl;
}
if( inputDevice != paNoDevice && outputDevice != paNoDevice ){
if( inputImpl != outputImpl )
return paDevicesMustBelongToTheSameDriverModel;
}
result = impl->openStream( stream, inputDevice, numInputChannels, inputSampleFormat, inputDriverInfo,
outputDevice, numOutputChannels, outputSampleFormat, outputDriverInfo,
sampleRate, framesPerBuffer, numberOfBuffers, streamFlags, callback, userData );
if( result == paNoError )
((PaStreamImplementation*)stream)->magic = PA_STREAM_MAGIC;
return result;
}
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData )
{
PaError result;
int inputDevice = driverModels[0]->getDefaultInputDeviceID;
int outputDevice = driverModels[0]->getDefaultOutputDeviceID;
result = driverModels[0]->openStream( stream, inputDevice, numInputChannels, sampleFormat, 0,
outputDevice, numOutputChannels, sampleFormat, 0,
sampleRate, framesPerBuffer, numberOfBuffers,
streamFlags, callback, userData );
if( result == paNoError )
((PaStreamImplementation*)stream)->magic = PA_STREAM_MAGIC;
return result;
}
PaError Pa_CloseStream( PortAudioStream* stream )
{
PA_CHECK_INITIALIZED;
PaError result = ((PaStreamImplementation*)stream)->close();
if( result == PaNoError )
((PaStreamImplementation*)stream)->magic = 0; /* clear magic number */
return result;
}
PaError Pa_StartStream( PortAudioStream *stream );
{
PA_CHECK_INITIALIZED;
return ((PaStreamImplementation*)stream)->start();
}
PaError Pa_StopStream( PortAudioStream *stream );
{
PA_CHECK_INITIALIZED;
return ((PaStreamImplementation*)stream)->stop();
}
PaError Pa_AbortStream( PortAudioStream *stream );
{
PA_CHECK_INITIALIZED;
return ((PaStreamImplementation*)stream)->abort();
}
PaError Pa_StreamActive( PortAudioStream *stream )
{
PA_CHECK_INITIALIZED;
return ((PaStreamImplementation*)stream)->active();
}
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
{
PA_CHECK_INITIALIZED;
return ((PaStreamImplementation*)stream)->time();
}
double Pa_StreamCPULoad( PortAudioStream* stream )
{
PA_CHECK_INITIALIZED;
return ((PaStreamImplementation*)stream)->cpuLoad();
}
int Pa_GetMinNumBuffers( PaDeviceID deviceID, int framesPerBuffer, double sampleRate )
{
PaDriverModelImplementation *impl;
PA_CHECK_INITIALIZED;
ConvertPublicDeviceIDToImplementationDeviceID( impl, deviceID );
if( impl == NULL )
return paInvalidDeviceID;
return impl->getMinNumBuffers( framesPerBuffer, sampleRate );
}
void Pa_Sleep( long msec )
{
same as existing implementaion
}
PaError Pa_GetSampleSize( PaSampleFormat format )
{
same as existing implementation
}
#endif /* PA_MULTIDRIVER */

View file

@ -0,0 +1,143 @@
#ifndef PA_MULTIDRIVERMODEL_H
#define PA_MULTIDRIVERMODEL_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*
This file contains the host-neutral code for implementing multiple driver model
support in PortAudio.
It has not been compiled, but it is supplied only for example purposes at this stage.
*/
#include "portaudio.h"
#define PA_MULTIDRIVER // for multidriver support
TODO: declare function pointer types for the following function pointers
/*
Each driver model implementation needs to implement an initialize function
which is added to the driverModelInitializers array in pa_multidrivermodel.c
the initializer function needs to return a pointer to a
PaDriverModelImplementation structure, or NULL if initiliazation failed. TODO: need error code instead
the function pointer members of this structure point to funtions
which operate in exactly the same way as the corresponding functions
in the PortAudio API.
*/
struct{
fptr terminate; /* takes the PaDriverModelImplementation* returned by initialize */
fptr getDriverModelInfo;
fptr getHostError;
fptr getHostErrorText;
fptr countDevices;
fptr getDefaultInputDeviceID;
fptr getDefaultOutputDeviceID;
fptr getDeviceInfo;
fptr openStream;
fptr getMinNumBuffers;
} PaDriverModelImplementation;
/*
whenever an implementaion's openstream method is called it should return a
PortAudioStream* whose first segment is actually the following structure.
the functions pointer members of this structure point to funcitons
which operate in exactly the same way as the corresponding functions
in the PortAudio API.
*/
struct{
unsigned long magic;
fptr close;
fptr start;
fptr stop;
fptr abort;
fptr active;
fptr time;
fptr cpuLoad;
} PaStreamImplementation;
/*
Implementations should set magic to PA_STREAM_MAGIC when opening
a stream _and_ clear it to zero when closing a stream.
All functions which operate on streams should check the validity
of magic.
*/
#define PA_STREAM_MAGIC 0x12345678
#define PA_CHECK_STREAM( stream )\
if( ((PaStreamImplementation*)stream)->magic != PA_STREAM_MAGIC )\
return paBadStreamPtr;
/*
PA_API allows the same implementation to be used for single
driver model and multi-driver model operation. If
PA_MULTIDRIVER not defined, PA_API will declare api
functions as global, otherwise they will be static, and include
the drivermodel code.
Usage would be something like:
int PA_API(CountDevices)();
The PA_MULTIDRIVER_SUPPORT macro declares the initialization and
termination functions required by the multidriver support. it also
allocates and deallocates the PaDriverModelImplementation structure.
TODO: add macros for initializing PaStreamImplementation PortAudioStream
these would be PA_INITIALIZE_STREAM and PA_TERMINATE_STREAM
they would assign and clear the magic number and assign the
interface functions if neceassary.
*/
#ifdef PA_MULTIDRIVER
#define PA_API( model, name ) static Pa ## model ## _ ## name
#define PA_MULTIDRIVER_SUPPORT( model )\
PaError Pa_ ## model ## _MultiDriverTerminate( PaStreamImplementation *impl )\
{\
free( impl );\
return Pa ## model ## _Terminate();\
}\
PaError Pa ## model ## _MultiDriverInitialize( PaStreamImplementation** impl )\
{\
PaError result = Pa ## model ## _Initialize();\
\
if( result == paNoError ){\
*impl = malloc( sizeof( PaDriverModelImplementation ) );\
if( impl == NULL ){\
// TODO: call terminate, return an error
}else{\
(*impl)->terminate = Pa ## model ## _MultiDriverTerminate();\
(*impl)->getDriverModelInfo = Pa ## model ## _GetDriverModelInfo();\
(*impl)->getHostError = Pa ## model ## _GetHostError();\
// TODO: assign the rest of the interface functions
}\
}\
return result;\
}
#else /* !PA_MULTIDRIVER */
#define PA_API( model, name ) Pa_ ## name
#define PA_MULTIDRIVER_SUPPORT
#endif /* PA_MULTIDRIVER */
#endif /* PA_MULTIDRIVERMODEL_H */

View file

@ -0,0 +1,197 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Internal docs. How a stream is started or stopped.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Implementation - Start/Stop</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
<a href="http://www.portaudio.com">PortAudio</a> Implementation Guide</h1></center>
</td>
</tr>
</table></center>
<p>This document describes how to implement the PortAudio API on a new
computer platform. Implementing PortAudio on a new platform, makes it possible
to port many existing audio applications to that platform.
<p>By Phil Burk
<br>Copyright 2000 Phil Burk and Ross Bencina
<p>Note that the license says: <b>"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."</b>.
So when you have finished a new implementation, please send it back to
us at&nbsp; "<a href="http://www.portaudio.com">http://www.portaudio.com</a>"
so that we can make it available for other users. Thank you!
<h2>
Download the Latest PortAudio Implementation</h2>
Always start with the latest implementation available at "<a href="http://www.portaudio.com">http://www.portaudio.com</a>".
Look for the nightly snapshot under the CVS section.
<h2>
Select an Existing Implementation as a Basis</h2>
The fastest way to get started is to take an existing implementation and
translate it for your new platform. Choose an implementation whose architecture
is as close as possible to your target.
<ul>
<li>
DirectSound Implementation - pa_win_ds - Uses a timer callback for the
background "thread". Polls a circular buffer and writes blocks of data
to keep it full.</li>
<li>
Windows MME - pa_win_wmme - Spawns an actual Win32 thread. Writes blocks
of data to the HW device and waits for events that signal buffer completion.</li>
<li>
Linux OSS - pa_linux - Spawns a real thread that writes to the "/dev/dsp"
stream using blocking I/O calls.</li>
</ul>
When you write a new implementation, you will be using some code that is
in common with all implementations. This code is in the folder "pa_common".
It provides various functions such as parameter checking, error code to
text conversion, sample format conversion, clipping and dithering, etc.
<p>The code that you write will go into a separate folder called "pa_{os}_{api}".
For example, code specific to the DirectSound interface for Windows goes
in "pa_win_ds".
<h2>
Read Docs and Code</h2>
Famialiarize yourself with the system by reading the documentation provided.
here is a suggested order:
<ol>
<li>
User Programming <a href="pa_tutorial.html">Tutorial</a></li>
<li>
Header file "pa_common/portaudio.h" which defines API.</li>
<li>
Header file "pa_common/pa_host.h" for host dependant code. This definces
the routine you will need to provide.</li>
<li>
Shared code in "pa_common/pa_lib.c".</li>
<li>
Docs on Implementation of <a href="pa_impl_startstop.html">Start/Stop</a>
code.</li>
</ol>
<h2>
Implement&nbsp; Output to Default Device</h2>
Now we are ready to crank some code. For instant gratification, let's try
to play a sine wave.
<ol>
<li>
Link the test program "pa_tests/patest_sine.c" with the file "pa_lib.c"
and the implementation specific file you are creating.</li>
<li>
For now, just stub out the device query code and the audio input code.</li>
<li>
Modify PaHost_OpenStream() to open your default target device and get everything
setup.</li>
<li>
Modify PaHost_StartOutput() to start playing audio.</li>
<li>
Modify PaHost_StopOutput() to stop audio.</li>
<li>
Modify PaHost_CloseStream() to clean up. Free all memory that you allocated
in PaHost_OpenStream().</li>
<li>
Keep cranking until you can play a sine wave using "patest_sine.c".</li>
<li>
Once that works, try "patest_pink.c", "patest_clip.c", "patest_sine8.c".</li>
<li>
To test your Open and Close code, try "patest_many.c".</li>
<li>
Now test to make sure that the three modes of stopping are properly supported
by running "patest_stop.c".</li>
<li>
Test your implementation of time stamping with "patest_sync.c".</li>
</ol>
<h2>
Implement Device Queries</h2>
Now that output is working, lets implement the code for querying what devices
are available to the user. Run "pa_tests/pa_devs.c". It should print all
of the devices available and their characteristics.
<h2>
Implement Input</h2>
Implement audio input and test it with:
<ol>
<li>
patest_record.c - record in half duplex, play back as recorded.</li>
<li>
patest_wire.c - full duplex, copies input to output. Note that some HW
may not support full duplex.</li>
<li>
patest_fuzz.c - plug in your guitar and get a feel for why latency is an
important issue in computer music.</li>
<li>
paqa_devs.c - try to open every device and use it with every possible format</li>
</ol>
<h2>
Debugging Tools</h2>
You generally cannot use printf() calls to debug real-time processes because
they disturb the timing. Also calling printf() from your background thread
or interrupt could crash the machine. So PA includes a tool for capturing
events and storing the information while it is running. It then prints
the events when Pa_Terminate() is called.
<ol>
<li>
To enable trace mode, change TRACE_REALTIME_EVENTS in "pa_common/pa_trace.h"
from a (0) to a (1).</li>
<li>
Link with "pa_common/pa_trace.c".</li>
<li>
Add trace messages to your code by calling:</li>
<br><tt>&nbsp;&nbsp; void AddTraceMessage( char *msg, int data );</tt>
<br><tt>for example</tt>
<br><tt>&nbsp;&nbsp; AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ",
past->past_NumCallbacks );</tt>
<li>
Run your program. You will get a dump of events at the end.</li>
<li>
You can leave the trace messages in your code. They will turn to NOOPs
when you change TRACE_REALTIME_EVENTS back to (0).</li>
</ol>
<h2>
Delivery</h2>
Please send your new code along with notes on the implementation back to
us at "<a href="http://www.portaudio.com">http://www.portaudio.com</a>".
We will review the implementation and post it with your name. If you had
to make any modifications to the code in "pa_common" or "pa_tests" <b>please</b>
send us those modifications and your notes. We will try to merge your changes
so that the "pa_common" code works with <b>all</b> implementations.
<p>If you have suggestions for how to make future implementations easier,
please let us know.
<br>THANKS!
<br>&nbsp;
</body>
</html>

View file

@ -0,0 +1,190 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.75 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Internal docs. How a stream is started or stopped.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Implementation - Start/Stop</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Implementation</h1></center>
</td>
</tr>
</table></center>
<h2>
Starting and Stopping Streams</h2>
PortAudio is generally executed in two "threads". The foreground thread
is the application thread. The background "thread" may be implemented as
an actual thread, an interrupt handler, or a callback from a timer thread.
<p>There are three ways that PortAudio can stop a stream. In each case
we look at the sequence of events and the messages sent between the two
threads. The following variables are contained in the internalPortAudioStream.
<blockquote><tt>int&nbsp;&nbsp; past_IsActive;&nbsp;&nbsp;&nbsp;&nbsp;
/* Background is still playing. */</tt>
<br><tt>int&nbsp;&nbsp; past_StopSoon;&nbsp;&nbsp;&nbsp;&nbsp; /* Stop
when last buffer done. */</tt>
<br><tt>int&nbsp;&nbsp; past_StopNow;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*
Stop IMMEDIATELY. */</tt></blockquote>
<h3>
Pa_AbortStream()</h3>
This function causes the background thread to terminate as soon as possible
and audio I/O to stop abruptly.
<br>&nbsp;
<table BORDER COLS=2 WIDTH="60%" >
<tr>
<td><b>Foreground Thread</b></td>
<td><b>Background Thread</b></td>
</tr>
<tr>
<td>sets <tt>StopNow</tt></td>
<td></td>
</tr>
<tr>
<td></td>
<td>sees <tt>StopNow</tt>,&nbsp;</td>
</tr>
<tr>
<td></td>
<td>clears IsActive, stops thread</td>
</tr>
<tr>
<td>waits for thread to exit</td>
<td></td>
</tr>
<tr>
<td>turns off audio I/O</td>
<td></td>
</tr>
</table>
<h3>
Pa_StopStream()</h3>
This function stops the user callback function from being called and then
waits for all audio data written to the output buffer to be played. In
a system with very low latency, you may not hear any difference between
<br>&nbsp;
<table BORDER COLS=2 WIDTH="60%" >
<tr>
<td><b>Foreground Thread</b></td>
<td><b>Background Thread</b></td>
</tr>
<tr>
<td>sets StopSoon</td>
<td></td>
</tr>
<tr>
<td></td>
<td>stops calling user callback</td>
</tr>
<tr>
<td></td>
<td>continues until output buffer empty</td>
</tr>
<tr>
<td></td>
<td>clears IsActive, stops thread</td>
</tr>
<tr>
<td>waits for thread to exit</td>
<td></td>
</tr>
<tr>
<td>turns off audio I/O</td>
<td></td>
</tr>
</table>
<h3>
User callback returns one.</h3>
If the user callback returns one then the user callback function will no
longer be called. Audio output will continue until all audio data written
to the output buffer has been played. Then the audio I/O is stopped, the
background thread terminates, and the stream becomes inactive.
<br>&nbsp;
<table BORDER COLS=2 WIDTH="60%" >
<tr>
<td><b>Foreground Thread</b></td>
<td><b>Background Thread</b></td>
</tr>
<tr>
<td></td>
<td>callback returns 1</td>
</tr>
<tr>
<td></td>
<td>sets StopSoon</td>
</tr>
<tr>
<td></td>
<td>stops calling user callback</td>
</tr>
<tr>
<td></td>
<td>continues until output buffer empty</td>
</tr>
<tr>
<td></td>
<td>clears IsActive, stops thread</td>
</tr>
<tr>
<td>waits for thread to exit</td>
<td></td>
</tr>
<tr>
<td>turns off audio I/O</td>
<td></td>
</tr>
</table>
<br>&nbsp;
</body>
</html>

View file

@ -0,0 +1,55 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.77 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Compiling for ASIO (Windows or Macintosh)</h2>
<blockquote>ASIO is a low latency audio API from Steinberg. To compile
an ASIO application, you must first <a href="http://www.steinberg.net/developers/ASIO2SDKAbout.phtml">download
the ASIO SDK</a> from Steinberg. You also need to obtain ASIO drivers for
your audio device.
<p>Note: I am using '/' as a file separator below. On Macintosh replace
'/' with ':'. On Windows, replace '/' with '\'.
<p>&nbsp;To use ASIO with the PortAudio library add the following source
files to your project:
<blockquote>
<pre>pa_asio/pa_asio.cpp</pre>
</blockquote>
and also these files from the ASIO SDK:
<blockquote>
<pre>common/asio.cpp
host/asiodrivers.cpp
host/asiolist.cpp</pre>
</blockquote>
and add these directories to the path for include files
<blockquote>
<pre>asiosdk2/host/pc&nbsp;&nbsp; (for Windows)
asiosdk2/common
asiosdk2/host</pre>
</blockquote>
You may try compiling the "pa_tests/patest_saw.c" file first because it
is the simplest.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_over.html">previous</a> |&nbsp; <a href="pa_tut_callback.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,91 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.77 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Writing a Callback Function</h2>
<blockquote>To write a program using PortAudio, you must include the "portaudio.h"
include file. You may wish to read "<a href="portaudio_h.txt">portaudio.h</a>"
because it contains a complete description of the PortAudio functions and
constants.
<blockquote>
<pre>#include "portaudio.h"</pre>
</blockquote>
The next task is to write your custom callback function. It is a function
that is called by the PortAudio engine whenever it has captured audio data,
or when it needs more audio data for output.
<p>Your callback function is often called by an interrupt, or low level
process so you should not do any complex system activities like allocating
memory, or reading or writing files, or printf(). Just crunch numbers and
generate audio signals. What is safe or not safe will vary from platform
to platform. On the Macintosh, for example, you can only call "interrupt
safe" routines. Also do not call any PortAudio functions in the callback
except for Pa_StreamTime() and Pa_GetCPULoad().
<p>Your callback function must return an int and accept the exact parameters
specified in this typedef:
<blockquote>
<pre>typedef int (PortAudioCallback)(
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void *inputBuffer, void *outputBuffer,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long framesPerBuffer,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PaTimestamp outTime, void *userData );</pre>
</blockquote>
Here is an example callback function from the test file "patests/patest_saw.c".
It calculates a simple left and right sawtooth signal and writes it to
the output buffer. Notice that in this example, the signals are of <tt>float</tt>
data type. The signals must be between -1.0 and +1.0. You can also use
16 bit integers or other formats which are specified during setup. You
can pass a pointer to your data structure through PortAudio which will
appear as <tt>userData</tt>.
<blockquote>
<pre>int patestCallback(&nbsp; void *inputBuffer, void *outputBuffer,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long framesPerBuffer,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PaTimestamp outTime, void *userData )
{
&nbsp;&nbsp;&nbsp; unsigned int i;
/* Cast data passed through stream to our structure type. */
&nbsp;&nbsp;&nbsp; paTestData *data = (paTestData*)userData;
&nbsp;&nbsp;&nbsp; float *out = (float*)outputBuffer;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp; for( i=0; i&lt;framesPerBuffer; i++ )
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp; /* Stereo channels are interleaved. */
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *out++ = data->left_phase;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* left */
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *out++ = data->right_phase;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* right */
&nbsp;&nbsp;&nbsp; /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data->left_phase += 0.01f;
&nbsp;&nbsp;&nbsp; /* When signal reaches top, drop back down. */
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
&nbsp;&nbsp;&nbsp; /* higher pitch so we can distinguish left and right. */
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data->right_phase += 0.03f;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; return 0;
}</pre>
</blockquote>
</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_over.html">previous</a> |&nbsp; <a href="pa_tut_init.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,65 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.75 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Querying for Available Devices</h2>
<blockquote>There are often several different audio devices available in
a computer with different capabilities. They can differ in the sample rates
supported, bit widths, etc. PortAudio provides a simple way to query for
the available devices, and then pass the selected device to Pa_OpenStream().
For an example, see the file "pa_tests/pa_devs.c".
<p>To determine the number of devices:
<blockquote>
<pre>numDevices = Pa_CountDevices();</pre>
</blockquote>
You can then query each device in turn by calling Pa_GetDeviceInfo() with
an index.
<blockquote>
<pre>for( i=0; i&lt;numDevices; i++ ) {
&nbsp;&nbsp;&nbsp;&nbsp; pdi = Pa_GetDeviceInfo( i );</pre>
</blockquote>
It will return a pointer to a <tt>PaDeviceInfo</tt> structure which is
defined as:
<blockquote>
<pre>typedef struct{
&nbsp;&nbsp;&nbsp; int structVersion;&nbsp;
&nbsp;&nbsp;&nbsp; const char *name;
&nbsp;&nbsp;&nbsp; int maxInputChannels;
&nbsp;&nbsp;&nbsp; int maxOutputChannels;
/* Number of discrete rates, or -1 if range supported. */
&nbsp;&nbsp;&nbsp; int numSampleRates;
/* Array of supported sample rates, or {min,max} if range supported. */
&nbsp;&nbsp;&nbsp; const double *sampleRates;
&nbsp;&nbsp;&nbsp; PaSampleFormat nativeSampleFormat;
}PaDeviceInfo;</pre>
</blockquote>
If the device supports a continuous range of sample rates, then numSampleRates
will equal -1, and the sampleRates array will have two values, the minimum&nbsp;
and maximum rate.
<p>The device information is allocated by Pa_Initialize() and freed by
Pa_Terminate() so you do not have to free() the structure returned by Pa_GetDeviceInfo().</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_util.html">previous</a> |&nbsp; <a href="pa_tut_rw.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,42 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.73 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Exploring PortAudio</h2>
<blockquote>Now that you have a good idea of how PortAudio works, you can
try out the test programs.
<ul>
<li>
For an example of playing a sine wave, see "pa_tests/patest_sine.c".</li>
<li>
For an example of recording and playing back a sound, see&nbsp; "pa_tests/patest_record.c".</li>
</ul>
I also encourage you to examine the source for the PortAudio libraries.
If you have suggestions on ways to improve them, please let us know. if
you want to implement PortAudio on a new platform, please let us know as
well so we can coordinate people's efforts.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> | <a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_rw.html">previous</a> |&nbsp; next</font>
</body>
</html>

View file

@ -0,0 +1,43 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.73 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Initializing PortAudio</h2>
<blockquote>Before making any other calls to PortAudio, you must call <tt>Pa_Initialize</tt>().
This will trigger a scan of available devices which can be queried later.
Like most PA functions, it will return a result of type <tt>paError</tt>.
If the result is not <tt>paNoError</tt>, then an error has occurred.
<blockquote>
<pre>err = Pa_Initialize();
if( err != paNoError ) goto error;</pre>
</blockquote>
You can get a text message that explains the error message by passing it
to
<blockquote>
<pre>printf(&nbsp; "PortAudio error: %s\n", Pa_GetErrorText( err ) );</pre>
</blockquote>
</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> | <a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_callback.html">previous</a> |&nbsp; <a href="pa_tut_open.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.77 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Compiling for Macintosh</h2>
<blockquote>To compile a Macintosh application with the PortAudio library,
add the following source files to your project:
<blockquote>
<pre>pa_mac:pa_mac.c
pa_common:pa_lib.c
pa_common:portaudio.h
pa_common:pa_host.h</pre>
</blockquote>
Also add the Apple <b>SoundLib</b> to your project.
<p>You may try compiling the "pa_tests:patest_saw.c" file first because
it is the simplest.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_over.html">previous</a> |&nbsp; <a href="pa_tut_callback.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,46 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Compiling for Macintosh OS X</h2>
<blockquote>To compile a Macintosh OS X CoreAudio application with the
PortAudio library:
<p>Create a new ProjectBuilder project. You can use a "Tool" project to
run the PortAudio examples.
<p>Add the following source files to your Project:
<blockquote>
<pre>pa_mac_core/pa_mac_core.c
pa_common/pa_lib.c
pa_common/portaudio.h
pa_common/pa_host.h
pa_common/pa_convert.c</pre>
</blockquote>
Add the Apple CoreAudio.framework to your project by selecting "Add FrameWorks..."
from the Project menu.
<p>Compile and run the "pa_tests:patest_saw.c" file first because it is
the simplest.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_over.html">previous</a> |&nbsp; <a href="pa_tut_callback.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,52 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.73 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Opening a Stream using Defaults</h2>
<blockquote>The next step is to open a stream which is similar to opening
a file. You can specify whether you want audio input and/or output, how
many channels, the data format, sample rate, etc. There are two calls for
opening streams, <tt>Pa_OpenStream</tt>() and <tt>Pa_OpenDefaultStream</tt>().
<p><tt>Pa_OpenStream()</tt> takes extra&nbsp; parameters which give you
more control. You can normally just use <tt>Pa_OpenDefaultStream</tt>()
which just calls <tt>Pa_OpenStream()</tt> <tt>with</tt> some reasonable
default values.&nbsp; Let's open a stream for stereo output, using floating
point data, at 44100 Hz.
<blockquote>
<pre>err = Pa_OpenDefaultStream(
&nbsp;&nbsp;&nbsp; &amp;stream,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* passes back stream pointer */
&nbsp;&nbsp;&nbsp; 0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* no input channels */
&nbsp;&nbsp;&nbsp; 2,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* stereo output */
&nbsp;&nbsp;&nbsp; paFloat32,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* 32 bit floating point output */
&nbsp;&nbsp;&nbsp; 44100,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* sample rate */
&nbsp;&nbsp;&nbsp; 256,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* frames per buffer */
&nbsp;&nbsp;&nbsp; 0,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* number of buffers, if zero then use default minimum */
&nbsp;&nbsp;&nbsp; patestCallback, /* specify our custom callback */
&nbsp;&nbsp;&nbsp; &amp;data );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* pass our data through to callback */</pre>
</blockquote>
If you want to use 16 bit integer data, pass <tt>paInt16</tt> instead of
<tt>paFloat32</tt>.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> | <a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_init.html">previous</a> |&nbsp; <a href="pa_tut_run.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,46 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.77 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Compiling for Unix OSS</h2>
<blockquote>[Skip this page if you are not using Unix and OSS]
<p>We currently support the <a href="http://www.opensound.com/">OSS</a>
audio drivers for Linux, Solaris, and FreeBSD. We hope to someday support
the newer ALSA drivers.
<ol>
<li>
cd to pa_unix_oss directory</li>
<li>
Edit the Makefile and uncomment one of the tests. You may try compiling
the "patest_sine.c" file first because it is very simple.</li>
<li>
gmake run</li>
</ol>
</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_pc.html">previous</a> |&nbsp; <a href="pa_tut_callback.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,92 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Overview of PortAudio</h2>
<blockquote>PortAudio is a library that provides streaming audio input
and output. It is a cross-platform API (Application Programming Interface)
that works on Windows, Macintosh, Unix running OSS, SGI, BeOS, and perhaps
other platforms by the time you read this. This means that you can write
a simple 'C' program to process or generate an audio signal, and that program
can run on several different types of computer just by recompiling the
source code.
<p>Here are the steps to writing a PortAudio application:
<ol>
<li>
Write a callback function that will be called by PortAudio when audio processing
is needed.</li>
<li>
Initialize the PA library and open a stream for audio I/O.</li>
<li>
Start the stream. Your callback function will be now be called repeatedly
by PA in the background.</li>
<li>
In your callback you can read audio data from the inputBuffer and/or write
data to the outputBuffer.</li>
<li>
Stop the stream by returning 1 from your callback, or by calling a stop
function.</li>
<li>
Close the stream and terminate the library.</li>
</ol>
</blockquote>
<blockquote>There is also <a href="pa_tut_rw.html">another interface</a>
provided that allows you to generate audio in the foreground. You then
simply write data to the stream and the tool will not return until it is
ready to accept more data. This interface is simpler to use but is usually
not preferred for large applications because it requires that you launch
a thread to perform the synthesis. Launching a thread may be difficult
on non-multi-tasking systems such as the Macintosh prior to MacOS X.
<p>Let's continue by building a simple application that will play a sawtooth
wave.
<p>Please select the page for the specific implementation you would like
to use:
<ul>
<li>
<a href="pa_tut_pc.html">Windows (WMME or DirectSound)</a></li>
<li>
<a href="pa_tut_mac.html">Macintosh SoundManager for OS 7,8,9</a></li>
<li>
<a href="pa_tut_mac_osx.html">Macintosh CoreAudio for OS X</a></li>
<li>
<a href="pa_tut_asio.html">ASIO on Windows or Macintosh</a></li>
<li>
<a href="pa_tut_oss.html">Unix OSS</a></li>
</ul>
or continue with the <a href="pa_tut_callback.html">next page of the programming
tutorial</a>.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tutorial.html">previous</a></font>
</body>
</html>

View file

@ -0,0 +1,78 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.77 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Compiling for Windows (WMME or DirectSound)</h2>
<blockquote>To compile PortAudio for Windows, you can choose between two
options. One implementation uses the DirectSound API. The other uses the
Windows MultiMedia Extensions API (aka WMME or WAVE).
<p>Some advantages of using DirectSound are that DirectSound may have lower
latency than WMME, and supports effects processing plugins. But one disadvantage
is that DirectSound is not installed on all PCs, and is not well supported
under Windows NT. So WMME is the best choice for most projects.
<p>For either implementation add the following source files to your project:
<blockquote>
<pre><b>pa_common\pa_lib.c
pa_common\portaudio.h
pa_common\pa_host.h</b></pre>
</blockquote>
Link with the system library "<b>winmm.lib</b>". For Visual C++:
<ol>
<li>
select "Settings..." from the "Project" menu,</li>
<li>
select the project name in the tree on the left,</li>
<li>
choose "All Configurations" in the popup menu above the tree,</li>
<li>
select the "Link" tab,</li>
<li>
enter "winmm.lib", without quotes, as the first item in the "Object/library
modules:" field.</li>
</ol>
<b>WMME</b> - To use the WMME implementation, add the following source
files to your project:
<blockquote><b><tt>pa_win_wmme/pa_win_wmme.c</tt></b></blockquote>
<b>DirectSound</b> - If you want to use the DirectSound implementation
of PortAudio then you must have a recent copy of the free
<a href="http://www.microsoft.com/directx/download.asp">DirectX</a>
SDK for Developers from Microsoft installed on your computer. To compile
an application add the following source files to your project:
<blockquote>
<pre><b>pa_win_ds\dsound_wrapper.c
pa_win_ds\pa_dsound.c</b></pre>
</blockquote>
Link with the system library "<b>dsound.lib</b>" using the procedure described
above for "winmm.lib".</blockquote>
<blockquote>You might try compiling the "pa_tests\patest_saw.c" file first
because it is the simplest.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_over.html">previous</a> |&nbsp; <a href="pa_tut_callback.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,56 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.73 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Starting and Stopping a Stream</h2>
<blockquote>The stream will not start running until you call Pa_StartStream().
Then it will start calling your callback function to perform the audio
processing.
<blockquote>
<pre>err = Pa_StartStream( stream );
if( err != paNoError ) goto error;</pre>
</blockquote>
At this point, audio is being generated. You can communicate to your callback
routine through the data structure you passed in on the open call, or through
global variables, or using other interprocess communication techniques.
Please be aware that your callback function may be called at interrupt
time when your foreground process is least expecting it. So avoid sharing
complex data structures that are easily corrupted like double linked lists.
<p>In many of the tests we simply sleep for a few seconds so we can hear
the sound. This is easy to do with Pa_Sleep() which will sleep for some
number of milliseconds. Do not rely on this function for accurate scheduling.
it is mostly for writing examples.
<blockquote>
<pre>/* Sleep for several seconds. */
Pa_Sleep(NUM_SECONDS*1000);</pre>
</blockquote>
When you are through, you can stop the stream from the foreground.
<blockquote>
<pre>err = Pa_StopStream( stream );
if( err != paNoError ) goto error;</pre>
</blockquote>
You can also stop the stream by returning 1 from your custom callback function.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> | <a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_open.html">previous</a> |&nbsp; <a href="pa_tut_term.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,79 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.77 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Blocking Read/Write Functions</h2>
<blockquote>[Note: These functions are not part of the official PortAudio
API. They are simply built on top of PortAudio as an extra utility. Also
note that they are under evaluation and their definition may change.]
<p>There are two fundamentally different ways to design an audio API. One
is to use callback functions the way we have already shown. The callback
function operates under an interrupt or background thread This leaves the
foreground application free to do other things while the audio just runs
in the background. But this can sometimes be awkward.
<p>So we have provided an alternative technique that lets a program generate
audio in the foreground and then just write it to the audio stream as if
it was a file. If there is not enough room in the audio buffer for more
data, then the write function will just block until more room is available.
This can make it very easy to write an audio example. To use this tool,
you must add the files "pablio/pablio.c" and "pablio/ringbuffer.c" to your
project. You must also:
<blockquote>
<pre>#include "pablio.h"</pre>
</blockquote>
Here is a short excerpt of a program that opens a stream for input and
output. It then reads a block of samples from input, and writes them to
output, in a loop.&nbsp; The complete example can be found in "pablio/test_rw.c".
<blockquote>
<pre>&nbsp;&nbsp;&nbsp; #define SAMPLES_PER_FRAME&nbsp;&nbsp;&nbsp;&nbsp; (2)
&nbsp;&nbsp;&nbsp; #define FRAMES_PER_BLOCK&nbsp;&nbsp;&nbsp; (1024)
&nbsp;&nbsp;&nbsp; SAMPLE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; samples[SAMPLES_PER_FRAME * FRAMES_PER_BLOCK];
&nbsp;&nbsp;&nbsp; PaError&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; err;
&nbsp;&nbsp;&nbsp; PABLIO_Stream&nbsp; *aStream;
/* Open simplified blocking I/O layer on top of PortAudio. */
&nbsp;&nbsp;&nbsp; err = OpenAudioStream( &amp;rwbl, SAMPLE_RATE, paFloat32,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (PABLIO_READ_WRITE | PABLIO_STEREO) );
&nbsp;&nbsp;&nbsp; if( err != paNoError ) goto error;
/* Process samples in the foreground. */
&nbsp;&nbsp;&nbsp; for( i=0; i&lt;(NUM_SECONDS * SAMPLE_RATE); i++ )
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp; /* Read one block of data into sample array from audio input. */
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ReadAudioStream( aStream, samples, FRAMES_PER_BLOCK );
&nbsp;&nbsp;&nbsp; /*
&nbsp;&nbsp;&nbsp; ** At this point you could process the data in samples array,
&nbsp;&nbsp;&nbsp; ** and write the result back to the same samples array.
&nbsp;&nbsp;&nbsp; */
&nbsp;&nbsp;&nbsp; /* Write that same frame of data to output. */
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WriteAudioStream( aStream, samples, FRAMES_PER_BLOCK );
&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; CloseAudioStream( aStream );</pre>
</blockquote>
</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_devs.html">previous</a> |&nbsp; <a href="pa_tut_explore.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,47 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.73 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Terminating PortAudio</h2>
<blockquote>You can start and stop a stream as many times as you like.
But when you are done using it, you should close it by calling:</blockquote>
<blockquote>
<blockquote>
<pre>err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;</pre>
</blockquote>
Then when you are done using PortAudio, you should terminate the whole
system by calling:
<blockquote>
<pre>Pa_Terminate();</pre>
</blockquote>
That's basically it. You can now write an audio program in 'C' that will
run on multiple platforms, for example PCs and Macintosh.
<p>In the rest of the tutorial we will look at some additional utility
functions, and a different way of using PortAudio that does not require
the use of a callback function.</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> | <a href="pa_tutorial.html">contents</a>
| <a href="pa_tut_run.html">previous</a> |&nbsp; <a href="pa_tut_util.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,55 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.75 [en]C-gatewaynet (Win98; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<h2>
Utility Functions</h2>
<blockquote>Here are several more functions that are not critical, but
may be handy when using PortAudio.
<p>Pa_StreamActive() returns one when the stream in playing audio, zero
when not playing, or a negative error number if the stream is invalid.
The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
but may also become inactive if the callback returns a non-zero value.
In the latter case, the stream is considered inactive after the last buffer
has finished playing.
<blockquote>
<pre>PaError Pa_StreamActive( PortAudioStream *stream );</pre>
</blockquote>
Pa_StreamTime() returns the number of samples that have been generated.
PaTimeStamp is a double precision number which is a convenient way to pass
big numbers around even though we only need integers.
<blockquote>
<pre>PaTimestamp Pa_StreamTime( PortAudioStream *stream );</pre>
</blockquote>
The "CPU Load" is a fraction of total CPU time consumed by the stream's
audio processing. A value of 0.5 would imply that PortAudio and the sound
generating callback was consuming roughly 50% of the available CPU time.
This function may be called from the callback function or the application.
<blockquote>
<pre>double Pa_GetCPULoad( PortAudioStream* stream );</pre>
</blockquote>
</blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> |
<a href="pa_tutorial.html">contents</a> | <a href="pa_tut_term.html">previous</a>
|&nbsp; <a href="pa_tut_devs.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,46 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="Tutorial for PortAudio, a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Tutorial</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio Tutorial</h1></center>
</td>
</tr>
</table></center>
<p>Copyright 2000 Phil Burk and Ross Bencina
<h2>
Table of Contents</h2>
<blockquote><a href="pa_tut_over.html">Overview of PortAudio</a>
<br><a href="pa_tut_mac.html">Compiling for Macintosh OS 7,8,9</a>
<br><a href="pa_tut_mac_osx.html">Compiling for Macintosh OS X</a>
<br><a href="pa_tut_pc.html">Compiling for Windows (DirectSound and WMME)</a>
<br><a href="pa_tut_asio.html">Compiling for ASIO on Windows or Mac OS
8,9</a>
<br><a href="pa_tut_oss.html">Compiling for Unix OSS</a>
<br><a href="pa_tut_callback.html">Writing a Callback Function</a>
<br><a href="pa_tut_init.html">Initializing PortAudio</a>
<br><a href="pa_tut_open.html">Opening a Stream using Defaults</a>
<br><a href="pa_tut_run.html">Starting and Stopping a Stream</a>
<br><a href="pa_tut_term.html">Cleaning Up</a>
<br><a href="pa_tut_util.html">Utilities</a>
<br><a href="pa_tut_devs.html">Querying for Devices</a>
<br><a href="pa_tut_rw.html">Blocking Read/Write Functions</a>
<br><a href="pa_tut_explore.html">Exploring the PortAudio Package</a></blockquote>
<font size=+2><a href="http://www.portaudio.com/">home</a> | contents |
previous |&nbsp; <a href="pa_tut_over.html">next</a></font>
</body>
</html>

View file

@ -0,0 +1,425 @@
#ifndef PORT_AUDIO_H
#define PORT_AUDIO_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.audiomulch.com/portaudio/
*
* Copyright (c) 1999-2000 Ross Bencina and 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.
*
*/
typedef int PaError;
typedef enum {
paNoError = 0,
paHostError = -10000,
paInvalidChannelCount,
paInvalidSampleRate,
paInvalidDeviceId,
paInvalidFlag,
paSampleFormatNotSupported,
paBadIODeviceCombination,
paInsufficientMemory,
paBufferTooBig,
paBufferTooSmall,
paNullCallback,
paBadStreamPtr,
paTimedOut,
paInternalError
} PaErrorNum;
/*
Pa_Initialize() is the library initialisation function - call this before
using the library.
*/
PaError Pa_Initialize( void );
/*
Pa_Terminate() is the library termination function - call this after
using the library.
*/
PaError Pa_Terminate( void );
/*
Return host specific error.
This can be called after receiving a paHostError.
*/
long Pa_GetHostError( void );
/*
Translate the error number into a human readable message.
*/
const char *Pa_GetErrorText( PaError errnum );
/*
Sample formats
These are formats used to pass sound data between the callback and the
stream. Each device has a "native" format which may be used when optimum
efficiency or control over conversion is required.
Formats marked "always available" are supported (emulated) by all devices.
The floating point representation uses +1.0 and -1.0 as the respective
maximum and minimum.
*/
typedef unsigned long PaSampleFormat;
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
#define paInt24 ((PaSampleFormat) (1<<3))
#define paPackedInt24 ((PaSampleFormat) (1<<4))
#define paInt8 ((PaSampleFormat) (1<<5))
#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
#define paCustomFormat ((PaSampleFormat) (1<<16))
/*
Device enumeration mechanism.
Device ids range from 0 to Pa_CountDevices()-1.
Devices may support input, output or both. Device 0 is always the "default"
device and should support at least stereo in and out if that is available
on the taget platform _even_ if this involves kludging an input/output
device on platforms that usually separate input from output. Other platform
specific devices are specified by positive device ids.
*/
typedef int PaDeviceID;
#define paNoDevice -1
typedef struct{
int structVersion;
const char *name;
int maxInputChannels;
int maxOutputChannels;
/* Number of discrete rates, or -1 if range supported. */
int numSampleRates;
/* Array of supported sample rates, or {min,max} if range supported. */
const double *sampleRates;
PaSampleFormat nativeSampleFormats;
} PaDeviceInfo;
int Pa_CountDevices();
/*
Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID()
Return the default device ID or paNoDevice if there is no devices.
The result can be passed to Pa_OpenStream().
On the PC, the user can specify a default device by
setting an environment variable. For example, to use device #1.
set PA_RECOMMENDED_OUTPUT_DEVICE=1
The user should first determine the available device ID by using
the supplied application "pa_devs".
*/
PaDeviceID Pa_GetDefaultInputDeviceID( void );
PaDeviceID Pa_GetDefaultOutputDeviceID( void );
/*
PaTimestamp is used to represent a continuous sample clock with arbitrary
start time useful for syncronisation. The type is used in the outTime
argument to the callback function and the result of Pa_StreamTime()
*/
typedef double PaTimestamp;
/*
Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
referring to the device specified by id.
If id is out of range the function returns NULL.
The returned structure is owned by the PortAudio implementation and must
not be manipulated or freed. The pointer is guaranteed to be valid until
between calls to Pa_Initialize() and Pa_Terminate().
*/
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID devID );
/*
PortAudioCallback is implemented by clients of the portable audio api.
inputBuffer and outputBuffer are arrays of interleaved samples,
the format, packing and number of channels used by the buffers are
determined by parameters to Pa_OpenStream() (see below).
framesPerBuffer is the number of sample frames to be processed by the callback.
outTime is the time in samples when the buffer(s) processed by
this callback will begin being played at the audio output.
See also Pa_StreamTime()
userData is the value of a user supplied pointer passed to Pa_OpenStream()
intended for storing synthesis data etc.
return value:
The callback can return a nonzero value to stop the stream. This may be
useful in applications such as soundfile players where a specific duration
of output is required. However, it is not necessary to utilise this mechanism
as StopStream() will also terminate the stream. A callback returning a
nonzero value must fill the entire outputBuffer.
NOTE: None of the other stream functions may be called from within the
callback function except for Pa_GetCPULoad().
*/
typedef int (PortAudioCallback)(
void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/*
Stream flags
These flags may be supplied (ored together) in the streamFlags argument to
the Pa_OpenStream() function.
[ suggestions? ]
*/
#define paNoFlag (0)
#define paClipOff (1<<0) /* disable defult clipping of out of range samples */
#define paDitherOff (1<<1) /* disable default dithering */
#define paPlatformSpecificFlags (0x00010000)
typedef unsigned long PaStreamFlags;
/*
A single PortAudioStream provides multiple channels of real-time
input and output audio streaming to a client application.
Pointers to PortAudioStream objects are passed between PortAudio functions.
*/
typedef void PortAudioStream;
#define PaStream PortAudioStream
/*
Pa_OpenStream() opens a stream for either input, output or both.
stream is the address of a PortAudioStream pointer which will receive
a pointer to the newly opened stream.
inputDevice is the id of the device used for input (see PaDeviceID above.)
inputDevice may be paNoDevice to indicate that an input device is not required.
numInputChannels is the number of channels of sound to be delivered to the
callback. It can range from 1 to the value of maxInputChannels in the
device input record for the device specified in the inputDevice parameter.
If inputDevice is paNoDevice numInputChannels is ignored.
inputSampleFormat is the format of inputBuffer provided to the callback
function. inputSampleFormat may be any of the formats described by the
PaSampleFormat enumeration (see above). PortAudio guarantees support for
the sound devices native formats (nativeSampleFormats in the device info
record) and additionally 16 and 32 bit integer and 32 bit floating point
formats. Support for other formats is implementation defined.
inputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
inputDriverInfo is never required for correct operation. If not used
inputDriverInfo should be NULL.
outputDevice is the id of the device used for output (see PaDeviceID above.)
outputDevice may be paNoDevice to indicate that an output device is not required.
numOutputChannels is the number of channels of sound to be supplied by the
callback. See the definition of numInputChannels above for more details.
outputSampleFormat is the sample format of the outputBuffer filled by the
callback function. See the definition of inputSampleFormat above for more
details.
outputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
outputDriverInfo is never required for correct operation. If not used
outputDriverInfo should be NULL.
sampleRate is the desired sampleRate for input and output
framesPerBuffer is the length in sample frames of all internal sample buffers
used for communication with platform specific audio routines. Wherever
possible this corresponds to the framesPerBuffer parameter passed to the
callback function.
numberOfBuffers is the number of buffers used for multibuffered
communication with the platform specific audio routines. This parameter is
provided only as a guide - and does not imply that an implementation must
use multibuffered i/o when reliable double buffering is available (such as
SndPlayDoubleBuffer() on the Macintosh.)
streamFlags may contain a combination of flags ORed together.
These flags modify the behavior of the
streaming process. Some flags may only be relevant to certain buffer formats.
callback is a pointer to a client supplied function that is responsible
for processing and filling input and output buffers (see above for details.)
userData is a client supplied pointer which is passed to the callback
function. It could for example, contain a pointer to instance data necessary
for processing the audio buffers.
return value:
Apon success Pa_OpenStream() returns PaNoError and places a pointer to a
valid PortAudioStream in the stream argument. The stream is inactive (stopped).
If a call to Pa_OpenStream() fails a nonzero error code is returned (see
PAError above) and the value of stream is invalid.
*/
PaError Pa_OpenStream( PortAudioStream** stream,
PaDeviceID inputDevice,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDevice,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PaStreamFlags streamFlags,
PortAudioCallback *callback,
void *userData );
/*
Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that
opens the default input and/or ouput devices. Most parameters have
identical meaning to their Pa_OpenStream() counterparts, with the following
exceptions:
If either numInputChannels or numOutputChannels is 0 the respective device
is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() )
sampleFormat applies to both the input and output buffers.
*/
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData );
/*
Pa_CloseStream() closes an audio stream, flushing any pending buffers.
*/
PaError Pa_CloseStream( PortAudioStream* );
/*
Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
Pa_StopStream() waits until all pending audio buffers have been played.
Pa_AbortStream() stops playing immediately without waiting for pending
buffers to complete.
*/
PaError Pa_StartStream( PortAudioStream *stream );
PaError Pa_StopStream( PortAudioStream *stream );
PaError Pa_AbortStream( PortAudioStream *stream );
/*
Pa_StreamActive() returns one when the stream is playing audio,
zero when not playing, or a negative error number if the
stream is invalid.
The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
but may also become inactive if the callback returns a non-zero value.
In the latter case, the stream is considered inactive after the last
buffer has finished playing.
*/
PaError Pa_StreamActive( PortAudioStream *stream );
/*
Pa_StreamTime() returns the current output time for the stream in samples.
This time may be used as a time reference (for example syncronising audio to
MIDI).
*/
PaTimestamp Pa_StreamTime( PortAudioStream *stream );
/*
The "CPU Load" is a fraction of total CPU time consumed by the
stream's audio processing.
A value of 0.5 would imply that PortAudio and the sound generating
callback was consuming roughly 50% of the available CPU time.
This function may be called from the callback function or the application.
*/
double Pa_GetCPULoad( PortAudioStream* stream );
/*
Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for
the current host based on minimum latency.
On the PC, for the DirectSound implementation, latency can be optionally set
by user by setting an environment variable.
For example, to set latency to 200 msec, put:
set PA_MIN_LATENCY_MSEC=200
in the AUTOEXEC.BAT file and reboot.
If the environment variable is not set, then the latency will be determined
based on the OS. Windows NT has higher latency than Win95.
*/
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
/*
Sleep for at least 'msec' milliseconds.
You may sleep longer than the requested time so don't rely
on this for accurate musical timing.
*/
void Pa_Sleep( long msec );
/*
Return size in bytes of a single sample in a given PaSampleFormat
or paSampleFormatNotSupported.
*/
PaError Pa_GetSampleSize( PaSampleFormat format );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PORT_AUDIO_H */

Binary file not shown.

View file

@ -0,0 +1,614 @@
<HTML>
<HEAD>
<TITLE>Proposed Changes to PortAudio API</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META content="Phil Burk, Ross Bencina" name=Author>
<META content="Changes being discussed by the community of PortAudio deveopers."
name=Description>
<META
content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,"
name=KeyWords>
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080">&nbsp;
<CENTER>
<TABLE bgColor=#fada7a cols=1 width="100%">
<TBODY>
<TR>
<TD>
<CENTER>
<H1>Proposed Changes to PortAudio API</H1>
</CENTER>
</TD></TR></TBODY></TABLE></CENTER>
<P><A href="http://www.portaudio.com/">PortAudio Home Page</A></P>
<P>Updated: January 11, 2002 </P>
<H2>Introduction</H2>
<P>This document describes modifications to the PortAudio API currently being considered by the developer community. It is intended to capture the current state of discussions. The authors are the various members of the PortAudio community and are too numerous to mention. Please refer to the mailing list archives to see who said what. </P>
<P>We are still at the design stage with all of these proposals - if you think something is missing, could be improved, or would just like to comment please do so on the PortAudio mailing list.</P>
<P>&nbsp; <A HREF="http://techweb.rfa.org/mailman/listinfo/portaudio">http://techweb.rfa.org/mailman/listinfo/portaudio</A> </P>
<H2>Contents</H2>
<UL>
<LI><A HREF="#Underflow">PortAudioCallback Underflow/Overflow Handling</A> </LI>
<LI><A HREF="#ImproveDeviceFormatQuerySystem">Improve Device Format Query System</A> * </LI>
<LI><A HREF="#Latency">Improve Latency Mechanisms</A> * </LI>
<LI><A HREF="#AllowCallbackVariableFramesPerBuffer">Allow Callbacks to Accept Variable framesPerBuffer</A> *</LI>
<LI><A HREF="#Blocking">Blocking Read/Write API</A> |C| </LI>
<LI><A HREF="#Interleaved">Non-Interleaved Buffers</A> </LI>
<LI><A HREF="#MultipleDriverModels">Support for Multiple Driver Models in a Single PortAudio Build</A> |C| </LI>
<LI><A HREF="#DriverModelSpecificPa_OpenStream">Driver Model Specific Pa_OpenStream() Parameters</A> |C| </LI>
<LI><A HREF="#Error">Handling of Host-Specific Error Codes</A> * </LI>
<LI><A HREF="#RenamePa_GetCPULoad">Rename Pa_GetCPULoad() and paInvalidDeviceId for Consistency</A> </LI>
<LI><A HREF="#CodingStyleGuidelines">Coding Style Guidelines</A> * </LI>
<LI><A HREF="#AdditionalPa_TerminateBehaviour">Additional Pa_Terminate() Behaviour</A> </LI>
<LI><A HREF="#ReviseInternalHostAPI">Revise Internal Host API</A> *</LI></UL>
<I><P>The proposals above which are marked with a * are under construction, those marked with |</I>C<I>| are essentially complete but require community approval or comment before they are considered complete. The remaining proposals are essentially complete and will be implemented as-is unless significant objections are raised.</P>
</I><H2>Status </H2>
<P>This document describes proposals that range in complexity from clarifications of existing API functionality, through to renaming API functions for consistency, through to significant feature additions. Due to the interdependency of many of the proposed changes, we plan to introduce all API changes simultaneously as part of release 19.</P>
<P>The developer community is in the process of reviewing all proposals. Most proposals are defined in sufficient detail to implement, however some completed proposals are dependent on proposals that are still under construction. Of the remaining unfinished proposals, only <A HREF="#ImproveDeviceFormatQuerySystem">Improve Device Format Query System</A>, <A HREF="#Latency">Improve Latency Mechanisms</A>, <A HREF="#AllowCallbackVariableFramesPerBuffer">Allow Callbacks to Accept Variable framesPerBuffer</A>, and <A HREF="#Error">Handling of Host-Specific Error Codes</A> require significant attention. Both the <A HREF="#CodingStyleGuidelines">Coding Style Guidelines</A> and <A HREF="#ReviseInternalHostAPI">Revise Internal Host API</A> proposals only effect implementors and do not effect the API definition itself. Refinement of these proposals is desirable but not critical to the development of release 19. Interested readers are advised to consult the Dependencies section of each proposal for more detailed information.</P>
<H2>Client Impact</H2>
<P>Some of the proposed changes will break existing client code. Usually these breakages involve renaming of constant identifiers or function calls. In a small number of cases additional parameters will be added to existing API functions. Concerned readers are advised to consult the Impact Analysis section of each proposal for further details.</P>
<P>____________</P>
<H2>PortAudioCallback&nbsp;<A NAME="Underflow"></A>Underflow/Overflow Handling</H2>
<H4>Status</H4>
<P>This proposal is sufficiently well defined to be implemented</P>
<H4>Background</H4>
<P>There are conditions where a full-duplex stream needs to generate output but doesn't have any input available, or where it has too much input so some input needs to be discarded (not passed to the output.) There is also the case where output is needed, but the callback has (transiently) consumed so much CPU time that output has to be generated without the callback being called.</P>
<P>Currently (V17) the PortAudioCallback Function handles these underflow/overflow conditions by passing NULL buffer pointers to the callback. This can happen if the output is pre-rolled and there is not yet any input data. It can also happen if the input underflows. </P>
<P>A number of concerns have been raised about the current system: For PortAudio to discard input just because it is not needed for output is considered unacceptable for some recording applications. Passing of NULL buffer pointers has been deemed to be too error prone and requires too much housekeeping for simple programs. </P>
<P>See <A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-October/000222.html">http://techweb.rfa.org/pipermail/portaudio/2001-October/000222.html</A> and subsequent messages in various threads.</P>
<P>This proposal seeks to keep the PortAudioCallback as simple as possible for ease-of-use while providing full access to overflow/underflow information, and all input and output sample data when clients require it.</P>
<H4>Proposal</H4>
<P>For streams providing input, the inputBuffer parameter will always point to a valid memory location containing framesPerBuffer frames of sample data in the requested format. The inputBuffer parameter will be NULL for output only streams. Similarly, the outputBuffer parameter will be NULL for input only streams, otherwise it will point to a valid memory location containing framesPerBuffer frames of sample data.</P>
<P>A new parameter will be added to the PortAudioCallback Function that gives the status of the data as bit flags. </P>
<TT><PRE>typedef int (PortAudioCallback)(</TT>
<TT>&nbsp; void *inputBuffer, void *outputBuffer,</TT>
<TT>&nbsp; unsigned long framesPerBuffer,</TT>
<TT>&nbsp; PaTimestamp outTime,
unsigned long&nbsp; statusFlags,
void *userData );</TT>
These bits may be set in the statusFlags parameter.
<TT>#define paInputUnderflow&nbsp;&nbsp; (1&lt;&lt;0) /* Input data is all zeros because no real data is available. */</TT>
#define paInputOverflow (1&lt;&lt;1) /* Input data was discarded by PortAudio */
<TT>#define </TT>paOutputUnderflow <TT>(1&lt;&lt;2) </TT>/* Output data was inserted by PortAudio because the callback is using too much CPU */
#define paOutputOverflow (1&lt;&lt;3) <TT>/* Output data will be ignored because no room is available. */</PRE>
</TT><P>&nbsp;</P>
<P>New rules will govern when the <TT>PortAudioCallback</TT> is called:</P>
<UL>
<LI>For input-only streams, the callback will be called for every available input buffer. If the callback takes too long to complete and input samples have to be discarded the paInputOverflow flag will be set the next time the callback is called. In input-only stream will never be called with the paInputUnderflow flag set. </LI>
<LI>For output-only streams, the callback will be called whenever an output buffer needs to be filled, except when doing so would cause the stream to fall further behind real-time due to CPULoad being too high. In such cases PortAudio will insert silence or repeat the previous buffer to the output and the paOutputUnderflow bit will be set next time the callback is called. An output-only stream will never be called with the paOutputOverflow flag set. </LI>
<LI>By default, a full duplex stream will behave according to the same rules as an output-only stream. If input is not available, the paInputUnderflow bit will be set and the input buffers will contain zeros. In the case of an input buffer overflow, PortAudio will discard input - in such cases the input samples will <B>not</B> be passed to a callback, and paInputOverflow will be set next time the callback is called to generate more output. A default full-duplex stream will never be called with the paOutputOverflow flag set. </LI>
<LI>A new mode flag <I>paNeverDropInput</I> to Pa_OpenStream() specifies that a full duplex stream will not discard overflowed input samples without calling the callback. When an input buffer overflow occurs, the callback will be called with the paOutputOverflow flag set, when the callback completes the output buffers will be discarded.</LI></UL>
<P>Note that the default full-duplex mode is intended to cover most common uses of PortAudio, where the client wants a simple audio streaming interface, and is happy to let PortAudio handle buffer underflow/overflow conditions when they occurs.</P>
<H4>Impact Analysis</H4>
<P>This proposal involves adding a new statusFlags parameter to PortAudioCallback. This will require all clients to update their callback implementations accordingly. Clients who previously checked for NULL buffers will be able to remove such checks.</P>
<P>____________</P>
<H2><A NAME="ImproveDeviceFormatQuerySystem">Improve Device Format Query System</A></H2>
<H4>Status</H4>
<P>This proposal is open for discussion.</P>
<H4>Background</H4>
<P>It has been noted that the current method (Pa_GetDeviceInfo()) of querying devices for supported sample formats, channels and sample rates is weak. It does not cleanly differentiate between 'PortAudio supported' formats and 'native' formats, and it is incapable of representing formats where the parameters are interdependent (eg where full duplex is only supported for certain sample rates.) We have also found that a static structure is not a good match for many driver models where format discovery is performed by polling the driver. Even if a sound card supports arbitrary sample rates, the driver model may only allow a client to poll to see whether a rate is available rather than providing the available rate ranges.</P>
<P>It has been noted that most (platform specific) audio APIs do a pretty bad job of allowing for device capability querying. Even the better APIs (ALSA is perhaps one) don't necessarily provide accurate information. This proposal should seek to maximise the amount of information that can be extracted from existing APIs while remaining expressive enough to take full advantage of APIs with more advanced capability querying systems should they become available in the future.</P>
<H4>Proposal</H4>
<P>A number of options are being considered with regard to supplying clients with format information:</P>
<P>Use PaDeviceInfo only for representing information that can be expressed without polling the driver multiple times, or only check for "standard" formats, or leave it unchanged.</P>
<P>And/Or</P>
<P>Add a Pa_IsFormatSupported() function:</P>
<PRE>Pa_IsFormatSupported( PaDeviceID inputDevice,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDevice,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate );</PRE>
<H4>Discussion</H4>
<P>At present it seems desirable to retain Pa_GetDeviceInfo() and the PaDeviceInfo structure. At a minimum PaDeviceInfo needs to contain name and driver model type code fields:</P>
<PRE>typedef struct{
int structVersion;
const char *name;
PaDriverModelTypeCode driverModel;
} PaDeviceInfo;</PRE>
<P>It was suggested that we could do things the way MME does: if Pa_OpenStream() is called with a NULL stream parameter then the stream isn't opened, but it is checked to see if the device supports the specified format - if the format is supported then paNoError would be returned, otherwise an error code would be returned. However, this has been ruled to be unsatisfactory, since querying for supported formats is really a different function from opening a stream.</P>
<H4>Impact Analysis</H4>
<P>This proposal will provide clients with more expressive methods for querying device capabilities, which should improve the utility of PortAudio. It is not yet clear what the full impact of this proposal will be.</P>
<P>____________</P>
<H2><A NAME="Latency"></A>Improve Latency Mechanisms</H2>
<H4>Status</H4>
<P>This proposal is open for discussion.</P>
<H4>Background</H4>
<P>The current mechanism for setting latency is not considered optimal by all clients of the API. There seems to be some tension between using the framesPerBuffer parameter of Pa_OpenStream as a latency control parameter and as a specifier for the number of frames supplied to the callback. Specifying latency as a single millisecond value would be more user friendly for some users, however some driver models need latency to be tuned by specifying buffer sizes and number of buffers. Additionally, it not clear whether separate input and output buffer counts would allow tuning of lower latencies in some circumstances.</P>
<P>A related issue is the need to improve the interface available to determine default latency parameters. The most recent proposal is documented below, however there is still some debate as to whether this is satisfactory. </P>
<P>See this thread: <A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-October/000196.html">http://techweb.rfa.org/pipermail/portaudio/2001-October/000196.html</A> </P>
<H4>Proposal</H4>
<P>The numBuffers parameter for Pa_OpenStream() could accept the following values in addition to the normal N&gt;0 values.&nbsp; Use PA_LATENCY_LOW for interactive performance, when response time is critical.&nbsp; Use PA_LATENCY_HIGH when playing sound files or other non-interactive uses, when glitch free operation is more critical. </P>
<PRE>
#define PA_LATENCY_WHATEVER&nbsp;&nbsp; (0) /* or _NO_OPINION or? */
#define PA_LATENCY_LOW&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (-1)&nbsp; /* For interactive performance. */
#define PA_LATENCY_HIGH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (-2)&nbsp; /* For playing sound files. */</PRE>
<P>Currently, when numBuffers&gt;0, Pa_OpenStream will constrain the actual numBuffers so that the latency is within a valid range determined by the driver, or an environment variables such as PA_MIN_LATENCY_MSEC. Propose changing the behaviour so that the requested value is honoured as much as possible. This will allow the user to override the minimum if they know their system can handle it. This might be used, for example on patched Linux kernels. By doing this we simplify the implementations and eliminate the need for adding a Pa_SetMinimumLatency() function. </P>
<PRE>
/* Negative return values are (double) paError */
double Pa_GetMinimumLatency( int deviceId, double frameRate );
double Pa_GetPreferredLatency( int deviceId, double frameRate );
double Pa_GetMaximumLatency( int deviceId, double frameRate );
/* the following would operate directly on streams */
double Pa_StreamInputLatency( PortAudioStream *stream );
double Pa_StreamOutputLatency(PortAudioStream *stream );</PRE>
<H4>Discussion</H4>
<P>This proposal does not yet address the question of whether there needs to be separate numInputBuffers and numOutputBuffers parameters for optimal latency specification.</P>
<P>____________</P>
<H2><A NAME="Blocking"><A NAME="AllowCallbackVariableFramesPerBuffer"></A>Allow Callbacks to Accept Variable framesPerBuffer</A></H2>
<H4>Status</H4>
<P>This proposal is under construction.</P>
<B><P>Background</B><BR>
<BR>
Some devices prefer, or even require, a particular value for framesPerBuffer. Some applications also prefer a particular buffer size because they may, for example, be doing FFTs. If possible, an integral number of application buffers can be fit into a device buffer. If an integral number of application buffers cannot be fit into an application buffer then some system for adapting between them is required. Stephane Letz implemented a block adapter for the ASIO driver that could perhaps be made part of a utility library for PortAudio.<BR>
<BR>
Adapting between different block size, unfortunately, involves extra memory movement and should be avoided. One way to avoid this is to allow an application to say "I can handle any value for framesPerBuffer". An example would be a simple algorithm like the one in "pa_tests/pa_fuzz.c". Its callback function could easily work with any value for FramesPerBuffer.<BR>
<BR>
<B>Proposal</B><BR>
<BR>
Pa_OpenStream() should be able to accept a zero value for framesPerBuffer. When framesPerBuffer is zero, the implementation can choose any value that it thinks will work best given the other constraints. That value will be passed to the user callback.<BR>
<BR>
To make the code more obvious, this constant could be used:<BR>
<BR>
<FONT FACE="Courier New" SIZE=2>#define paFramesPerBufferUnspecified (0)</FONT><BR>
<BR>
In order to find out what value the implementation chose, and also to help the application determine the actual latency, the following calls could be added:<BR>
<BR>
<FONT FACE="Courier New" SIZE=2>int Pa_GetFramesPerBuffer( PortAudioStream* stream );<BR>
int Pa_GetNumOutputBuffers( PortAudioStream* stream );<BR>
int Pa_GetNumInputBuffers( PortAudioStream* stream );</FONT><BR>
</P>
<P>____________</P>
<H2>Blocking Read/Write API</H2>
<H4>Status</H4>
<P>This proposal is open for comment.</P>
<H4>Background</H4>
<P>Many PortAudio users have requested a blocking read()/write() API that will be supported in addition to the current callback based API. A blocking Read/Write API would allow a more natural style of multi-threaded programming, and facilitate single-threaded reactive applications, while insulating clients from platform-specific thread synchronisation facilities. </P>
<P>A blocking read/write API would also be useful when binding PortAudio to languages that don't easily support callbacks such as Python, Java, Lisp and Smalltalk. However in this case it has been noted that a blocking API is not sufficient - the host language also needs to support native threads to interact efficiently with blocking. Dannenberg observes that native thread support cannot be added without major redesign (based on a study of Python and Squeak), but given blocking calls, there are several ways to structure programs using non-native threads.</P>
<P>Adding a blocking Read/Write interface to PortAudio has been discussed on a number of occasions, including the following threads:</P>
<P><A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-August/000063.html">http://techweb.rfa.org/pipermail/portaudio/2001-August/000063.html</A> a long thread about blocking calls.</P>
<P><A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-August/000137.html">http://techweb.rfa.org/pipermail/portaudio/2001-August/000137.html</A> this is Roger Dannenberg's proposal and a subsequent discussion</P>
<P><A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-August/000144.html">http://techweb.rfa.org/pipermail/portaudio/2001-August/000144.html</A> is a thread discussing using blocking APIs with other languages</P>
<H4>Proposal</H4>
<P>If a NULL callback parameter is passed to Pa_OpenStream() then the stream will be opened in blocking mode. This enables users to call Pa_WriteStream() and Pa_ReadStream() to read and write sample data. (The PaErrorNum item "paNullCallback" becomes obsolete.)</P>
<P>Pa_WriteStream() writes a buffer of frames to a stream. The length of the buffer is arbitrary and specified by the frames parameter. Pa_WriteStream() returns when all samples have been copied from buffer. If necessary, Pa_WriteStream() will wait until buffer space becomes available. (Waiting on Unix will be the by-product of an I/O system call, waiting in Win32 will be implemented by waiting on an Event object, and waiting on MacOS 9 will probably require a busy wait.) High performance applications will want to match the length of the buffer to framesPerBuffer, but this is not a requirement.</P>
<P>The buffer parameter has the same semantics and format as the inputbuffer and outputbuffer parameters of a PortAudioCallback function. In particular, non-interleaved data is handled in the same way.</P>
<P>Pa_ReadStream() is similar, but it reads rather than writes. </P>
<PRE>PaError Pa_WriteStream( PortAudioStream *stream,
void *buffer,
unsigned long frames );
PaError Pa_ReadStream( PortAudioStream *stream,
void *buffer,
unsigned long frames );</PRE>
<P>Pa_ReadStream() returns <I>paInputUnderflow</I> if input data was discarded by PortAudio after the previous call and before this call. Pa_WriteStream() returns <I>paOutputUnderflow</I> if output data was inserted after the previous call and before this call. The mode flag <I>paNeverDropInput</I> is ignored because Pa_ReadStream() and Pa_WriteStream() are not synchronized.</P>
<P>There are two functions to determine the number of frames available for writing and reading. These functions may be called to determine whether calls to Pa_WriteStream() or Pa_ReadStream() will return immediately or will wait. The return value, if non-negative, is the maximum number of frames that can be written or read without blocking or busy waiting. A negative value is a PaErrorNum.</P>
<PRE>long Pa_StreamWriteAvailable( PortAudioStream *stream );
long Pa_StreamReadAvailable( PortAudioStream *stream );</PRE>
<P>The stream functions Pa_CloseStream(), Pa_StartStream(), Pa_StopStream(), Pa_AbortStream(), Pa_StreamActive(), and Pa_StreamTime() work with the blocking API as well as with callbacks. Pa_StreamCPULoad() does not work with the blocking API. PortAudio might be extended to give applications access to the internal routines that compute Pa_StreamCPULoad(). Applications using blocking calls could then bracket audio computation with these calls to determine the CPU load. (This additional functionality is not being proposed here.)</P>
<H4>Discussion</H4>
<P>A rejected alternative is to allow Pa_WriteStream() and Pa_ReadStream() to return the number of frames actually written or read so that a Mac implementation could return immediately and avoid blocking. This would require applications to be prepared to handle partial read/writes. It seems simpler and more consistent to use "Available" to determine in advance whether blocking or busy waiting will occur if that is a concern. Also, note that data is almost certainly copied; however, it seems likely that the copy will be folded into any format conversion.</P>
<P>Implementations may want to provide a way for applications to be notified when data can be written or read. For example, one might want to know the file ID of an ALSA or OSS stream for use in a select() system call. Since this sort of information will be platform-specific and non-portable, no interface is defined here, but implementations can include a device-model-specific access function. If applications commonly need this information, we can think about how to make this more standardized.</P>
<H4>Implementation Notes</H4>
<P>Implementing blocking i/o will be quite simple for driver models where the underlying API is blocking-based. Under Windows (MME), the arrival of a buffer will signal an Event passed to waveOutOpen(). Pa_WriteStream() and Pa_ReadStream() will do all the work (no server threads necessary). Writes will make waveOutWrite calls. When no buffer is available, the writer will wait on the event and try again. Reading is similar. On the Mac, a double-buffer scheme can be set up where the Mac callbacks pick up data placed in buffers by Pa_WriteStream(). The double-buffer adds to the latency. Alternatively (and preferably) callbacks can be used only for notification, and Pa_WriteStream() can issue all the calls to write samples.</P>
<P>PABLIO currently contains a busy-wait ring buffer in "ringbuffer.c" which is generic, used in many projects and is pretty solid. This code could be a useful starting point for implementing the new blocking API on some platforms.</P>
<P>Pa_WriteStream() and Pa_ReadStream() are not thread safe. Applications wanting to call these from multiple threads should manage their own mutual exclusion. <I>[Roger: Is any of PortAudio thread safe? I don't think so. This is good because it avoids many system calls for mutual exclusion.]</P>
</I><P>At one time it was suggested that implementations only implement blocking calls and that callbacks would be required to implement callbacks in terms of the blocking API. The current direction is that this decision should be made independently for each driver-model.</P>
<H4>Impact Analysis</H4>
<P>This proposal would extend the functionality of PortAudio without requiring any changes to client code with the exception that the PaErrorNum paNullCallback will no longer be defined. As noted above, implementation complexity is dependent on the target platform.</P>
<P>____________</P>
<H2>Non-<A NAME="Interleaved"></A>Interleaved Buffers</H2>
<H4>Status</H4>
<P>This proposal is sufficiently well defined to be implemented. No objections have been raised.</P>
<H4>Background</H4>
<P><A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-October/000210.html">http://techweb.rfa.org/pipermail/portaudio/2001-October/000210.html</A> </P>
<P>Some native APIs use non-interleaved buffers, particularly those that support N&gt;2 channels. Additionally, many client applications use non-interleaved buffers internally. In order to avoid adding unnecessary overhead, PortAudio should support both interleaved and non-interleaved buffers on all platforms. </P>
<P>The current PortAudio/ASIO implementation works as follows : ASIO native buffers are non-interleaved and the de-interleaving, format conversion and copying the data into PortAudio interleaved buffers is done in one loop. But if PortAudio supported non-interleaved buffers then we could use efficient vector operations even for native buffer &lt;==&gt; port audio buffers transfers. </P>
<H4>Proposal</H4>
<P>A new sample format could be defined: </P>
<PRE>#define paNonInterleaved ((PaSampleFormat) (1&lt;&lt;31)) </PRE>
<P>&nbsp;</P>
<P>This could be used as a modifier flag to the buffer format fields of Pa_OpenStream(). When present, this flag would indicate that non-interleaved buffers would be passed to the callback. When not present, interleaved buffers would be used as is currently always the case. For example, the following code would open an interleaved stream:</P>
<PRE>
Pa_OpenStream(&amp;stream,
paNoDevice,
0,
paFloat32
NULL,
Pa_GetDefaultOutputDeviceID(),
2,
paFloat32,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0,
paClipOff,
patestCallback,
&amp;data );</PRE>
<P>And the following code would open a non-interleaved stream:</P>
<PRE>
Pa_OpenStream(&amp;stream,
paNoDevice,
0,
paFloat32|paNonInterleaved,
NULL,
Pa_GetDefaultOutputDeviceID(),
2,
paFloat32|paNonInterleaved,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0,
paClipOff,
patestCallback,
&amp;data );</PRE>
<P>In the user callback, the application would be passed a pointer to an array of buffers. The left and right buffers of a non-interleaved stream could be accessed as follows: </P>
<PRE>
float *left = ((float **) inputBuffer)[0];
float *right = ((float **) inputBuffer)[1];</PRE>
<P>This new sample format could also be used to interrogate the native driver to see if it supports interleaved or non-interleaved buffers. This would be achieved by reading the nativeSampleFormats field of the PaDeviceInfo structure. </P>
<H4>Impact Analysis</H4>
<P>This proposal extends the functionality of PortAudio without any impact on existing client code. It will require new conversion functions and all existing PortAudio implementations will have to be modified to reference these new conversion functions.</P>
<P>____________</P>
<H2><A NAME="MultipleDriverModels">Support For Multiple Driver Models</A> in a Single Build</H2>
<H4>Status</H4>
<P>This proposal is currently being discussed. Its final form depends on a number of other unfinished proposals.</P>
<H4>Dependencies </H4>
<P>The API changes described in this proposal are independent of all other proposals. However, changes to the host error mechanism defined in <A HREF="#Error">Handling of Host-Specific Error Codes</A>, and the addition of new API functions due to the <A HREF="#Blocking">Blocking Read/Write API</A> proposal may effect the implementation of this proposal. Changes to the API defined by the <A HREF="#ImproveDeviceFormatQuerySystem">Improve Device Format Query System</A> and the <A HREF="#Latency">Improve the Latency Setting Mechanism</A> proposals will need to be multi-driver-model capable. </P>
<H4>Background</H4>
<P>As the number of supported driver models available on each platform grows (WMME, ASIO, and DirectSound under Windows for example) client applications need to be able to select between different driver models at run-time. At least four platforms supported by PortAudio have multiple native driver models. At present PortAudio allows clients to link in support for at most one driver model.</P>
<P>This proposal is based this email: <A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-December/000308.html">http://techweb.rfa.org/pipermail/portaudio/2001-December/000308.html</A></P>
<H4>Requirements</H4>
<P>It will be necessary to supply clients with a method of displaying a textual description of the driver model for each PortAudio device.</P>
<P>Some PortAudio functions do not operate on PortAudioStreams, but rather they operate on or return the global state of the PortAudio library as a whole. If multiple driver-models were present, some of these functions would have different implementations, or would return different values depending on which the driver model they applied to. These functions include:</P>
<PRE> int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
PaDeviceID Pa_GetDefaultInputDeviceID( void );
PaDeviceID Pa_GetDefaultOutputDeviceID( void );
</PRE>
<P>Note that Pa_CountDevices() could also be interpreted as applying to a specific driver model.</P>
<P>PortAudio currently implements a per-driver-model extension mechanism via the inputDriverInfo and outputDriverInfo parameters to Pa_OpenStream(). For code to take advantage of driver-model-specific extensions when multiple driver models are present there needs to be a way to establish which (statically named) driver model is associated with each device. This is because driver-model-specific extensions should only be used in combination with devices supplied by that driver model.</P>
<P>PortAudio should present clients with all of the devices made available by each driver model. This means that some physical devices may be accessible though multiple driver models. Since it will not be possible to open a full duplex stream with input and output devices from different driver models, some clients may want to enumerate the available driver models and only display devices from one driver model at a time.</P>
<P>Introducing a new PortAudioDriverModel abstraction could fulfil the above requirements. At a minimum, this abstraction would need to have the following attributes:</P>
<UL>
<LI>A textual description for display on user interfaces </LI>
<LI>Functions for querying default device ids and latency parameters for each driver model </LI>
<LI>A unique identifier published in portaudio.h for use in code which takes advantage of driver model specific extensions</LI></UL>
<P>Additionally, the following features could be present:</P>
<UL>
<LI>A method for querying the default PortAudioDriverModel (perhaps the "best" driver model which has more than 0 devices available) </LI>
<LI>A method for enumerating 'currently available' PortAudioDriverModels. 'Currently available' could be interpreted as meaning the driver models linked into the current PortAudio implementation, or it could mean the driver models which currently have more than zero devices available. </LI>
<LI>A method of enumerating PortAudioDevices independently for each available PortAudioDriverModel</LI></UL>
<H4>Proposal</H4>
<P>This proposal consists of the 7 modifications to the PortAudio API listed below.</P>
<P>1. Define a new PaDriverModelTypeID enumeration, with fixed values for each driver model:</P>
<PRE>
/*
The PaDriverModelTypeID enumeration contains constants for uniquely
Identifying each driver model supported by PortAudio. This type
is used in the PaDriverModelInfo structure below. Driver model type ids
are guaranteed to never change, thus allowing code to be written that
conditionally uses driver model specific extensions.
New type ids will only be allocated when support for a new driver
model reaches "public alpha" status, prior to that developers should
use the paInDevelopment type id.
*/
typedef enum {
paInDevelopment=0, /* use while developing support for a new driver model */
paWin32DirectSound=1,
paWin32MME=2,
paWin32ASIO=3,
paMacOSSoundManager=4,
paMacOSCoreAudio=5,
paMacOSASIO=6,
paOSS=7,
paALSA=8,
paIRIXAL=9,
paBeOS=10
}PaDriverModelTypeID;</PRE>
<P>2. Define a method for enumerating driver models:</P>
<PRE>
/*
Driver model enumeration mechanism.
Driver model ids range from 0 to Pa_CountDriverModels()-1.
The default driver model is the lowest common denominator
driver model on the current platform and is unlikely to provide
the best performance.
*/
typedef int PaDriverModelID;
#define paDefaultDriverModel 0;
struct{
int structVersion;
PaDriverModelTypeID typeID; /* the well known unique identifier of this driver model */
const char *name; /* a textual description of the driver model for display on user interfaces */
}PaDriverModelInfo;
PaDriverModelID Pa_CountDriverModels();</PRE>
<P>3. Provide a method for retrieving information about a given driver model:</P>
<PRE>
/*
Pa_ GetDriverModelInfo () returns a pointer to an immutable
PaDriverModelInfo structure referring to the device specified by driverModelID.
If driverModelID is out of range the function returns NULL. The returned structure
is owned by the PortAudio implementation and must not be manipulated or
freed. The pointer is only guaranteed to be valid between calls
to Pa_Initialize() and Pa_Terminate().
*/
const PaDriverModelInfo* Pa_GetDriverModelInfo( PaDriverModelID driverModelID );</PRE>
<P>4. Add a new field to PaDeviceInfo to identify the driver model type:</P>
<PRE>
struct{
...
PaDriverModelID driverModel; /* note this is the run-time id */
...
}PaDeviceInfo;</PRE>
<P>This would enable the following two code fragments to be written.</P>
<PRE>
/* obtain the user-readable name of a device's driver model */
Pa_GetDriverModelInfo( deviceInfo-&gt;driverModel )-&gt;name;
/* implement special behavior for a specific driver model */
if( Pa_GetDriverModelInfo( deviceInfo-&gt;driverModel )-&gt;typeID == paWin32MME ){
InitialiseMMESpecificDeviceInfo();
}</PRE>
<P>5. Provide methods for finding per-driver model default devices and latency settings:</P>
<PRE>
PaDeviceID Pa_DriverModelDefaultInputDeviceID( PaDriverModelID driverModelID );
PaDeviceID Pa_DriverModelDefaultOutputDeviceID( PaDriverModelID driverModelID );
int Pa_DriverModelMinNumBuffers( PaDriverModelID driverModelID, int framesPerBuffer, double sampleRate );</PRE>
<P>6. Provide functions for enumerating devices on a per-driver-model basis. Note that this functionality is provided in addition to the current Pa_CountDevices() and Pa_GetDeviceInfo() functions:</P>
<PRE>
int Pa_DriverModelCountDevices( PaDriverModelID driverModelID );
PaDeviceID Pa_DriverModelGetDeviceID( PaDriverModelID driverModelID, int perDriverModelIndex );</PRE>
<P>7. Re-implement the following existing functions to use the default driver model. This would be a backwards compatible change except for Pa_GetMinNumBuffers() which gains an extra parameter.</P>
<PRE>
PaDeviceID Pa_GetDefaultInputDeviceID( void ); /* returns the default device id for the default driver model */
PaDeviceID Pa_GetDefaultOutputDeviceID( void );
int Pa_GetMinNumBuffers( PaDeviceID id, int framesPerBuffer, double sampleRate );</PRE>
<P>Note that Pa_GetMinNumBuffers() takes a device id, not a driver model id. This minimises the need for clients to be aware of the multiple driver model extensions. </P>
<H4>Discussion</H4>
<P>The main disadvantage of this proposal it that it may make the API seem more complex for new users.</P>
<P>There is concern that this proposal is too complex, and that the simpler solution of simply adding a driverModel string to the device info structure of each device would be sufficient. It is true that the simple solution would allow clients to duplicate the functionality of this proposal, provided driverModel strings were published and guaranteed not to change in the future. However, the bulk of the functionality included in this proposal will need to be implemented internally to facilitate multiple driver model support anyway. This proposal is based on the assumption that it is better to expose such functionality in the PortAudio API rather than require clients to reimplement what is already present internally. </P>
<P>There has been discussion about supporting "pluggable" driver models - the general idea is that a client application could link against PortAudio and PortAudio would load the available Driver Models at run-time using "PortAudio Driver Model Plugins." Some people consider this to be an overly complex solution, and no significant advantages over a monolithic PortAudio dll have been submitted yet. Some people would like PortAudio to always be able to be statically linked with multiple driver model support.</P>
<P>The overhead (both processor and memory) of the Multiple Driver Model support should be minimised on platforms which don't have multiple driver models (such as BeOS and some handheld devices.) Essentially this means that the functionality provided in the multiple driver model fa&ccedil;ade should be easy to duplicate.</P>
<H4>Implementation Notes</H4>
<P>The implementation will follow the methodology currently employed in PortMIDI described here: <A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-December/000295.html">http://techweb.rfa.org/pipermail/portaudio/2001-December/000295.html</A></P>
<P>An implementation of the driver model neutral "Fa&ccedil;ade" of this proposal exists here: </P>
<P><A HREF="http://www.portaudio.com/docs/pa_drivermodel.c.txt">http://www.portaudio.com/docs/pa_drivermodel.c.txt</A></P>
<P><A HREF="http://www.portaudio.com/docs/pa_drivermodel.h.txt">http://www.portaudio.com/docs/pa_drivermodel.h.txt</A></P>
<P>This proposal will involve the changes described below. Note that the string &lt;DM&gt; will be replaced with a driver model tag for each implementation.</P>
<P>Each driver model will have it's own Initialize function which PortAudio will call in response to client calls to Pa_Initialize and Pa_Terminate respectively. This will be the only identifier each driver model implementation will be required to expose.</P>
<PRE>PaError Pa&lt;DM&gt;_MultiDriverInitialize( PaDriverModelImplementation **impl );</PRE>
<P>PaDriverModelImplementation is an internal data structure containing a set of function pointers for globally relevant functions: (function pointer type declarations omitted for simplicity:)</P>
<PRE>struct{
fptr terminate; /* takes the PaDriverModelImplementation* returned by initialize */
fptr getDriverModelInfo;
fptr getHostError;
fptr getHostErrorText;
fptr countDevices;
fptr getDefaultInputDeviceID;
fptr getDefaultOutputDeviceID;
fptr getDeviceInfo;
fptr openStream;
fptr getMinNumBuffers;
} PaDriverModelImplementation;</PRE>
<P>The function pointers in PaDriverModelImplementation will point to the corresponding functions in current PortAudio implementations. The new multiple driver model support code will take care of mapping per-driver model device ids onto a single homogenous driver id range. A significant advantage of this scheme is that it will require very little change to existing PortAudio implementations.</P>
<P>A new PaStreamImplementation internal data structure will be defined to contain function pointers to implementations of the stream functions for each driver model. This structure will be placed at the head of implementation-specific data structures returned as PortAudioStream* in current implementations.</P>
<PRE>struct{
unsigned long magic;
fptr close;
fptr start;
fptr stop;
fptr abort;
fptr read;
fptr write;
fptr readAvailable;
fptr writeAvailable;
fptr active;
fptr time;
fptr cpuLoad;
} PaStreamImplementation;</PRE>
<P>Magic contains a unique bit pattern which should be set by implementations when a stream is opened, and cleared when it is closed. This technique will allow implementations to perform some degree of validation on PortAudioStream* passed to PortAudio.</P>
<H4>Impact Analysis</H4>
<P>This proposal will significantly improve the utility of PortAudio by allowing clients to support multiple driver models in a single executable. </P>
<P>The only required change for existing clients will to insert an extra deviceID parameter into calls to Pa_GetMinNumBuffers().</P>
<P>Since multiple driver models may return devices with the same names, a minimum requirement for clients who want to be "multiple driver model aware" will be to ensure that the appropriate driver model name is displayed alongside device names in the user interface.</P>
<P>____________</P>
<H2><A NAME="DriverModelSpecificPa_OpenStream">Driver Model Specific Pa_OpenStream() Parameters</A></H2>
<H4>Status</H4>
<P>This proposal is essentially complete, but is pending the final definition of PaDriverModelTypeID (see below.) </P>
<H4>Dependencies</H4>
<P>If the PaDriverModelSpecificStreamInfo structure defined in this proposal includes a PaDriverModelTypeID driver model identifier, then this proposal depends on the <A HREF="#MultipleDriverModels">Support for Multiple Driver Models in a Single PortAudio Build</A> proposal to define the form of the identifier.</P>
<H4>Background</H4>
<P>Pa_OpenStream has always had the inputDriverInfo and outputDriverInfo parameters, which were defined to support passing driver-model specific information to PortAudio implementations. Currently these parameters are defined as void* and are not used by any implementation. Two uses of inputDriverInfo and outputDriverInfo are planned for the near future: passing device names to OSS drivers, and passing additional device ids for opening multichannel soundcards under MME.</P>
<H4>Proposal</H4>
<P>The following structure could be defined and be placed at the head of all data structures passed to the inputDriverInfo and outputDriverInfo parameters of Pa_OpenStream:</P>
<PRE>struct{
unsigned long size; /* size of whole structure including this header */
PaDriverModelTypeID driverModel; /* driver model for which this data is intended */
unsigned long version; /* structure version */
}PaDriverModelSpecificStreamInfo;</PRE>
<P>The following driver model specific extensions should be placed in separate header files rather than being placed in portaudio.h</P>
<P>___</P>
<P>The following structure is proposed for passing device names to the OSS implementation:</P>
<PRE>struct{
PaDriverModelSpecificStreamInfo header;
char *deviceName;
}PaOSSSpecificStreamInfo;</PRE>
<P>A pointer to this structure could be passed to Pa_OpenStream() to request that a device other than the default dsp device be opened. This structure could be used for opening input and/or output devices.</P>
<P>___</P>
<P>The following structure is proposed for passing multiple interleaved device ids to the MME implementation in order to open a multichannel stream with some soundcards that support multichannel operation via multiple stereo (or other number of channels) interleaved devices. When this structure is passed, the MME implementation would ignore the deviceId parameters passed directly to Pa_OpenStream(), however it would not ignore the channelCount parameters.</P>
<P>#define paWMMEPassMultipleInterleavedBuffers 0x01 /* a flag */</P>
<PRE>struct{
PaDriverModelSpecificStreamInfo header;
int *deviceIdsAndChannelCounts; /* interleaved deviceIds and channelCounts */
int deviceCount;
int flags;
}PaMMESpecificStreamInfo;</PRE>
<P>The deviceIdsAndChannelCounts field points to an array of ints containing multiple {deviceId, channelCount} pairs. The number of integer elements in the array must be two times the value of the deviceCount field. Specified deviceIds must have a driver model type of Windows MME. Currently only one flag is defined: paWMMEPassMultipleInterleavedBuffers, this can be used to request that the raw, multiple interleaved buffers be passed to the callback.</P>
<H4>Discussion</H4>
<P>The type of the inputDriverInfo and outputDriverInfo parameters could be changed to PaDriverModelSpecificStreamInfo* however this may cause more trouble that it's worth.</P>
<P>The PaMMESpecificStreamInfo functionality may require the common buffer conversion functions defined in the <A HREF="#ReviseInternalHostAPI">Revise Internal Host API</A> proposal to support (multiple interleave &lt;==&gt; unified interleave) and (multiple interleave &lt;==&gt; non-interleaved) conversions.</P>
<H4>Impact Analysis</H4>
<P>This proposal provides access to new platform-specific extensions. No existing client code will be modified. Only implementations that implement the extensions will be effected.</P>
<P>____________</P>
<H2><A NAME="Error"></A>Handling of Host-Specific Error Codes</H2>
<H4>Status</H4>
<P>This proposal is sufficiently well defined to be implemented immediately. However, the possibility of extending the scope of this proposal is being discussed in this thread: <A HREF="http://techweb.rfa.org/pipermail/portaudio/2002-January/000358.html">http://techweb.rfa.org/pipermail/portaudio/2002-January/000358.html</A></P>
<H4>Background</H4>
<P>Currently the PaHostError error code is used to notify clients that a platform-specific error condition occurred. This is considered ambiguous and difficult to work with. </P>
<H4>Proposal</H4>
<P>PortAudio should seek to avoid returning ambiguous paHostError error codes, and instead translate to portable PortAudio error codes instead. In the case of the pa_win_mme implementation this means translating the following MME error codes:</P>
<P>MMSYSERR_ALLOCATED to paDeviceBusy (new) <BR>
MMSYSERR_BADDEVICEID to paInvalidDeviceId (already defined) <BR>
MMSYSERR_NODRIVER to paDriverMissing (new) <BR>
MMSYSERR_NOMEM to paInsufficientMemory (already defined) <BR>
WAVERR_BADFORMAT to paSampleFormatNotSupported (already defined) </P>
<H4>Discussion</H4>
<P>It is suggested that all implementations should be audited for their use of PaHostError.</P>
<P>There was some concern about polluting the PortAudio error code namespace with platform-specific error codes, and of the potential overhead of including platform specific error strings on other platforms. Another suggestion has been to add a Pa_GetHostErrorText() function.</P>
<P>If all error codes are mapped to PortAudio error codes do we need a PaHostError code and the Pa_GetHostErrorCode() function?</P>
<P>A suggestion has been made to extend Pa_GetErrorText() so that it retrieved driver model specific error strings when a host error occurs.</P>
<H4>Impact Analysis</H4>
<P>This proposal improves the quality of PortAudio diagnostics. Client code that depends on paHostError code to flag certain conditions may be effected.</P>
<P>____________</P>
<H2><A NAME="RenamePa_GetCPULoad">Rename Pa_GetCPULoad() </A>and paInvalidDeviceId for Consistency</H2>
<H4>Status</H4>
<P>Proposal is sufficiently well defined to be implemented. No objections have been raised.</P>
<H4>Background</H4>
<P>PortAudio functions that return global information typically have names of the form Pa_Get*() (eg. Pa_GetDeviceInfo). Almost all functions operating on PortAudio streams conform to the following implicit naming convention: Pa_*Stream( stream ) performs an action on a stream (eg Pa_StartStream() ) and Pa_Stream*( stream ) returns a stream attribute (eg. Pa_StreamTime() ). The only exception to this convention is the confusingly named Pa_GetCPULoad() which actually returns the CPU consumption of a particular stream's callback. </P>
<P>PortAudio functions and parameter names that operate on integer identifiers use the string ID (all uppercase.) The paInvalidDeviceId error code is an exception to this convention.</P>
<H4>Proposal</H4>
<P>Rename PaGetCPULoad() to Pa_StreamCPULoad() in order to conform to the established naming conventions.</P>
<P>Rename the paInvalidDeviceId error code to paInvalidDeviceID to conform to the established naming conventions.</P>
<H4>Impact Analysis</H4>
<P>This proposal improves the consistency of the naming scheme making the API easier to learn and remember. All clients which call Pa_GetCPULoad() will need to alter their code by renaming the function call. Clients who explicitly check for the paInvalidDeviceId error code will have to edit its capitalisation.</P>
<P>____________</P>
<H2><A NAME="CodingStyleGuidelines">Coding Style Guidelines</A></H2>
<H4>Status</H4>
<P>This proposal is under construction. Further suggestions and comments would be extremely welcome.</P>
<H4>Background</H4>
<P>Since the PortAudio code is commonly edited on many different platforms using different editors it has been suggested that some conventions be adopted to improve readability and consistency. The general opinion is that the definition of such conventions, and their enforcement shouldn't be too extreme. There are also a number of unspoken implementation standards that could be usefully written down. This proposal consists of a list of mechanical formatting conventions, and a list of "quality of implementation" conventions. When completed this proposal will packaged with the distribution and placed on the web site as "Coding Style Guidelines for PortAudio Implementors."</P>
<H4>Proposal</H4>
<P>The following formatting conventions should be adhered to in all PortAudio code:</P>
<UL>
<LI>TABs should not be used in .c and .h files; instead 4 spaces should be used. Makefiles should continue to use TABs since this is required by Make </LI>
<LI>Adopt a consistent set of rules for placement of braces (see note regarding Astyle below) </LI>
<LI>Adopt a consistent policy for line-end characters. This could be 'use CR on Mac, CRLF on Windows and LF on Unix' or it could be 'use Unix style new-lines in all source files.</LI></UL>
<P>AStyle ( <A HREF="http://astyle.sourceforge.net/">http://astyle.sourceforge.net/</A> ) has been proposed as helpful tool for cleaning code, however we haven't yet decided whether to use it on an ongoing basis. Once our style guidelines have been established it is expected that contributors of each implementation will take responsibility for keeping their code clean, as an automated tool applied by someone unfamiliar with the code will probably just mess things up.</P>
<P>In addition to the formatting conventions noted above, the following "quality of implementation" coding guidelines are being proposed to establish a quality baseline for our implementations:</P>
<UL>
<LI>All code should be written in C++-compatible plain ANSI-C (i.e. no // comments). The following guideline has been proposed: should compile silently with both "gcc -ansi -pedantic -Wall" and "g++ -ansi -pedantic -Wall" </LI>
<LI>Think of PortAudio as a heavyweight library rather than a lightweight wrapper. Always code defensively. Efficiency is important where it matters (eg in real-time callbacks) but safety is important everywhere. </LI>
<LI>All parameters passed to PortAudio by the user should be validated, and error codes returned where necessary. All reasonable efforts should be made to minimise the risk of a crash resulting from passing incorrect parameters to PortAudio. </LI>
<LI>Error handling should be complete. Every host function that can return an error condition should have its status checked. PortAudio may attempt to recover from errors, but generally error codes should be returned to the client. </LI>
<LI>In almost all cases, a PortAudio error code should be preferred to returning PaHostError. If a new PortAudio error code is needed it should be discussed with the maintainer to coordinate updating port_audio.h </LI>
<LI>PortAudio code should not cause resource leaks. After Pa_Terminate() is called, implementations should guarantee that all dynamically allocated resources have been freed. </LI>
<LI>The definition of the PortAudio API should minimise "implementation defined behaviour". For example, calling functions such as Pa_Initialize() after PortAudio is initialised, or Pa_Terminate() after PortAudio has been terminated should have well defined behaviour. (In this example, both calls should be safe and simply return an error code.) </LI>
<LI>Minimise dependence on ANSI C runtime on platforms where it would have to be loaded separately (eg on Win32 prefer Win32 API functions such as GlobalAlloc() to ANSI C functions such as malloc().) <I>(Ross: this may be problematic, since some portable parts of the implementation may need to allocate memory -- perhaps we should define our own internal memory allocation functions.)</LI></UL>
</I><P>It has been suggested that we make an effort to minimise the use of global and static data in PortAudio implementations. Another related goal is to reduce name pollution in the global scope. Some possible guidelines in this regard are:</P>
<UL>
<LI>Implementations should avoid exporting any symbols except where absolutely necessary. Specifically, global data must be declared statically. We will need a naming convention for non-public global symbols, such as internal functions defined in one module and used in another. </LI>
<LI>Implementations should minimise their use of static data. </LI></UL>
<H4>Discussion</H4>
<P>There will always be time to improve these guidelines, however we are making a concerted effort to document some standards before the next round of changes are implemented.</P>
<H4>Impact Analysis</H4>
<P>This proposal will require all existing code to be reviewed and possibly revised. This is not expected to be a "big-bang" operation. Rather it is envisaged that this will be a long term, ongoing process aimed at improving the quality of the PortAudio codebase. </P>
<P>____________</P>
<H2><A NAME="AdditionalPa_TerminateBehaviour">Additional Pa_Terminate() Behaviour</A></H2>
<H4>Status</H4>
<P>This proposal is sufficiently well defined to be implemented. </P>
<H4>Background</H4>
<P>Some driver models (eg ASIO, MME and DirectSound under Windows NT) can require a reboot to free devices when they are not closed properly (due to a program not calling Pa_Close() either in error, or due to a crash.) As a quality of implementation issue PortAudio should seek to avoid such circumstances.</P>
<H4>Proposal</H4>
<P>The definition of Pa_Terminate() should be extended as follows:</P>
<PRE>/*
Pa_Terminate() is the library termination function - call this after
using the library. This function deallocates all resources allocated
by PortAudio since it was initializied using Pa_Initialize(). Any open
PortAudioStreams are closed.
Pa_Terminate() MUST be called before exiting a program which
uses PortAudio. Failure to do so may result in serious resource
leaks, such as audio devices not being available until the next reboot.
*/
PaError Pa_Terminate( void );</PRE>
<H4>Implementation Notes</H4>
<P>One possible implementation strategy would be to add a "next" member to the internal stream data structure thus making it a linked list node, which could be linked into a list of all open streams.</P>
<H4>Discussion</H4>
<P>Some concerns have been raised about the overhead involved in PortAudio having to keep track of which streams are currently open. </P>
<P>There has been some discussion about the behaviour of nesting multiple calls to Pa_Initialize() and Pa_Terminate() - there is no intention of changing the current behaviour, which is that PortAudio has two states: "Initialized" and "Uninitialized" - in the Initialized state, Pa_Initialize() does nothing and returns an error, in the Uninitialized state Pa_Terminate() does nothing and returns an error.</P>
<H4>Impact Analysis</H4>
<P>This proposal changes the termination behaviour of PortAudio to reduce the likelihood of resource leaks.</P>
<P>On Windows, the new Pa_Terminate() behaviour would allow users who want full protection against device leakage to install a global Win32 exception handler that calls Pa_Terminate() before exiting when a crash occurs. Similar techniques (using SIGNAL handlers perhaps?) may be possible on other platforms where necessary.</P>
<P>____________</P>
<H2><A NAME="ReviseInternalHostAPI">Revise Internal Host API</A></H2>
<H4>Status</H4>
<P>This proposal is under construction. Ideally, someone should go through all of the existing implementations and identify code that could be factored into a common library (parameter validation code for example.)</P>
<H4>Dependencies</H4>
<P>This proposal is dependent on the <A HREF="#MultipleDriverModels">Support for Multiple Driver Models in a Single PortAudio Build</A> proposal, and the <A HREF="#Interleaved">Non-Interleaved Buffers</A> proposal. The proposed conversion functions may be dependent on the MME multichannel via stereo pairs extension which is part of the <A HREF="#DriverModelSpecificPa_OpenStream">Driver Model Specific Pa_OpenStream() Parameters</A> proposal.</P>
<H4>Background</H4>
<P>PortAudio defines a set of helper functions that all implementations share. It is envisaged that these internal functions will need to be revised in response to the changes proposed in this document. It would also be beneficial to take this opportunity to refactor any other common code fragments that could be shared by multiple implementations.</P>
<P>A refactoring of the buffer data conversion functions was proposed here: <A HREF="http://techweb.rfa.org/pipermail/portaudio/2001-November/000244.html">http://techweb.rfa.org/pipermail/portaudio/2001-November/000244.html</A> However the proposal below is not quite the same. A significant benefit of formally specifying the interface to the buffer conversion functions is that it would facilitate the creation of optimised assembly language versions for different platforms.</P>
<H4>Proposal</H4>
<P>A common set of buffer conversion functions should be defined and shared by all implementations. The buffer conversion functions should handle all permutations of:</P>
<UL>
<LI>Sample format </LI>
<LI>Channels </LI>
<LI>Interleave / Non-interleave </LI>
<LI>Endianness </LI>
<LI>Channel compensation</LI></UL>
<P>"Channel-compensation" is necessary when certain devices require a higher number of channels than the user requests. With the Midiman Delta1010, for example, the device always needs to be fed 10 channels of output and you must read 12 channels of input (at least under ALSA without the "plug" interface).</P>
<P>The conversion functions could look something like:</P>
<PRE>void ConversionFunction_DestType_DestInterleave_SrcType_SrcInterleave_ ( void *dest, int destChannels, void *src, int srcChannels, int frames );</PRE>
<P>The dest and src parameters have the same format as those supplied to the PortAudio client callback.</P>
<P>Rather than have each implementation call these conversion functions directly, a 'factory function' could be implemented that returns a pointer to a conversion function based on parameters specifying the format of the source and destination buffers. This factory function could be called as needed when a stream is opened. The conversion functions could then be made static and hidden from the rest of PortAudio. The 'factory function' could have the following form:</P>
<PRE>enum PaEndiannes { paBigEndian, paSmallEndian, paHostEndian };
PaBufferConversionFunction* Pa_GetBufferConversionFunction(
PaSampleFormat destFormat, int destChannels, PaEndianness destEndianness,
PaSampleFormat srcFormat, int srcChannels, PaEndianness srcEndianness );</PRE>
<P>Note that the interleave/deinterleave status is encoded in the destFormat and srcFormat parameters. paHostEndian is used to represent the endianness of the current platform since some driver models (eg ASIO) allow the driver to use samples in a different endianness from the host endianness. Another alternative is to encode sample endianness in PaSampleFormat - this would allow clients to write sample data of either endianness to PortAudio (e.g. soundfile playback direct from file) and benefit from PortAudio's byte swapping code.</P>
<P>The redundant use of channel parameters in both the conversion functions and the factory function is intentional and would allow channel-optimised conversion functions to be supplied for common cases such as 16-bit stereo.</P>
<H4>Discussion</H4>
<P>This proposal currently only addresses buffer conversion functions, however it is important to identify other common code fragments that could be placed in the shared PortAudio library.</P>
<P>This proposal has not yet addressed the fact that the conversion functions also need to handle clipping and dithering.</P>
<P>It is not clear whether additional conversion functions will be needed to accommodate the MME interleaved stereo pairs for multichannel devices proposal.</P>
<P>It hasn't been established whether PortAudio will be extended to support all PaSampleFormats on all devices.</P>
<P>It isn't clear whether paCustomFormat is viable under this proposal, or how it would be accommodated.</P>
<P>When the client requested format and the host format are different a temporary buffer may be required to hold the converted data. However, in general PortAudio should aim to convert data in-place. Functions may be needed to establish when temporary buffers are needed, and to allocate them.</P>
<P>Due to a mismatch between the API buffer size and the PortAudio callback buffer size some driver models require PortAudio to shuffle data among multiple buffers in order to fulfil client requests - this has not yet been considered within the current proposal.</P>
<P>Memory allocation should probably be handled with platform specific functions such as Win32 GlobalAlloc() rather than using malloc()</P>
<H4>Impact Analysis</H4>
<P>This proposal only effects PortAudio implementors. Increasing the utility of shared code will improve the quality of all PortAudio implementations in terms of speed, size, and robustness. It should also reduce the effort involved in porting PortAudio to a new driver model.</P>
<P>____________</P></BODY>
</HTML>

View file

@ -0,0 +1,339 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="PortAudio is a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Release Notes</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio - Release Notes</h1></center>
</td>
</tr>
</table></center>
<p>Link to <a href="http://www.portaudio.com">PortAudio Home Page</a>
<h2>
<b>V18 - 5/6/02</b></h2>
<blockquote>All source code and documentation now under <a href="http://www.portaudio.com/usingcvs.html">CVS</a>.
<p>Ran most of the code through <a href="http://astyle.sourceforge.net/">AStyle</a>
to cleanup ragged indentation caused by using different editors. Used this
command:
<br><tt>&nbsp;&nbsp; astyle --style=ansi -c -o --convert-tabs --indent-preprocessor
*.c</tt></blockquote>
<blockquote>Added "pa_common/pa_convert.c" for Mac OS X. Start of new conversion
utilities.
<p><b>ASIO</b>
<ul>
<li>
New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables,</li>
<li>
Cleanup of Pa_ASIO_Callback_Input</li>
<li>
Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo</li>
<li>
Deallocate all resources in PaHost_Term for cases where Pa_CloseStream
is not called properly</li>
<li>
New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows.
Allows use by multiple threads.</li>
<li>
Correct error code management in PaHost_Term, removed various compiler
warning</li>
<li>
Add Mac includes for &lt;Devices.h> and &lt;Timer.h></li>
<li>
Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better
error handling</li>
</ul>
<b>Mac OS X</b>
<ul>
<li>
Major cleanup and improvements.</li>
<li>
Fixed device queries for numChannels and sampleRates,</li>
<li>
Audio input works if using same CoreAudio device (some HW devices make
separate CoreAudio devices).</li>
<li>
Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file.</li>
<li>
Return error if opened in mono mode cuz not supported.</li>
<li>
Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally.</li>
<li>
Use getrusage() instead of gettimeofday() for CPU Load calculation.</li>
</ul>
<b>Windows MME</b>
<ul>
<li>
Fixed bug that caused TIMEOUT in Pa_StopStream(). Added check for past_StopSoon()
in Pa_TimeSlice(). Thanks Julien Maillard.</li>
<li>
Detect Win XP versus NT, use lower latency.</li>
<li>
Fix DBUG typo;</li>
<li>
removed init of CurrentCount which was not compiling on Borland</li>
<li>
general cleanup, factored streamData alloc and cpu usage initialization</li>
<li>
stopped counting WAVE_MAPPER when there were no audio cards plugged in</li>
</ul>
<b>Windows DirectSound</b>
<ul>
<li>
Detect Win XP and Win 2K properly when determining latency.</li>
</ul>
<b>Unix OSS</b>
<ul>
<li>
Use high real-time priority if app is running with root priveledges. Lowers
latency.</li>
<li>
Added watch dog thread that prevents real-time thread from hogging CPU
and hanging the computer.</li>
<li>
Check error return from read() and write().</li>
<li>
Check CPU endianness instead of assuming Little Endian.</li>
</ul>
</blockquote>
<h2>
<b>V17 - 10/15/01</b></h2>
<blockquote><b>Unix OSS</b>
<ul>
<li>
Set num channels back to two after device query for ALSA. This fixed a
bug in V16 that sometimes caused a failure when querying for the sample
rates. Thanks Stweart Greenhill.</li>
</ul>
</blockquote>
<blockquote>
<h4>
<b>Macintosh Sound Manager</b></h4>
<ul>
<li>
Use NewSndCallBackUPP() for CARBON compatibility.</li>
</ul>
</blockquote>
<h2>
<b>V16 - 9/27/01</b></h2>
<blockquote><b>Added Alpha implementations for ASIO, SGI, and BeOS!</b>
<br>&nbsp;
<li>
CPULoad is now calculated based on the time spent to generate a known number
of frames. This is more accurate than a simple percentage of real-time.
Implemented in pa_unix_oss, pa_win_wmme and pa_win_ds.</li>
<li>
Fix dither and shift for recording PaUInt8 format data.</li>
<li>
Added "patest_maxsines.c" which tests <tt>Pa_GetCPULoad().</tt></li>
</blockquote>
<blockquote>
<h4>
Windows WMME</h4>
<ul>
<li>
sDevicePtrs now allocated using <tt>GlobalAlloc()</tt>. This prevents a
crash in Pa_Terminate() on Win2000. Thanks Mike Berry for finding this.
Thanks Mike Berry.</li>
<li>
Pass process instead of thread to <tt>SetPriorityClass</tt>(). This fixes
a bug that caused the priority to not be increased. Thanks to Alberto di
Bene for spotting this.</li>
</ul>
<h4>
Windows DirectSound</h4>
<ul>
<li>
Casts for compiling with __MWERKS__ CodeWarrior.</li>
</ul>
<h4>
UNIX OSS</h4>
<ul>
<li>
Derived from Linux OSS implementation.</li>
<li>
Numerous patches from Heiko Purnhagen, Stephen Brandon, etc.</li>
<li>
Improved query mechanism which often bailed out unnecessarily.</li>
<li>
Removed sNumDevices and potential related bugs,</li>
<li>
Use <tt>getenv("PA_MIN_LATENCY_MSEC")</tt> in code to set desired latency.
User can set by entering:</li>
<br>&nbsp;&nbsp;&nbsp; <tt>export PA_MIN_LATENCY_MSEC=40</tt></ul>
<h4>
Macintosh Sound Manager</h4>
<ul>
<li>
Pass unused event to WaitNextEvent instead of NULL to prevent Mac OSX crash.
Thanks Dominic Mazzoni.</li>
<li>
Use requested number of input channels.</li>
<br>&nbsp;</ul>
</blockquote>
<h2>
<b>V15 - 5/29/01</b></h2>
<blockquote>
<ul>
<li>
<b>New Linux OSS Beta</b></li>
</ul>
<h4>
Windows WMME</h4>
<ul>
<li>
&nbsp;sDevicePtrs now allocated based on sizeof(pointer). Was allocating
too much space.</li>
<li>
&nbsp;Check for excessive numbers of channels. Some drivers reported bogus
numbers.</li>
<li>
Apply Mike Berry's changes for CodeWarrior on PC including condition including
of memory.h, and explicit typecasting on memory allocation.</li>
</ul>
<h4>
Macintosh Sound Manager</h4>
<ul>
<li>
ScanInputDevices was setting sDefaultOutputDeviceID instead of sDefaultInputDeviceID.</li>
<li>
Device Scan was crashing for anything other than siBadSoundInDevice, but
some Macs may return other errors! Caused failure to init on some G4s under
OS9.</li>
<li>
Fix TIMEOUT in record mode.</li>
<li>
Change CARBON_COMPATIBLE to TARGET_API_MAC_CARBON</li>
</ul>
</blockquote>
<h2>
<b>V14 - 2/6/01</b></h2>
<blockquote>
<ul>
<li>
Added implementation for Windows MultiMedia Extensions (WMME) by Ross and
Phil</li>
<li>
Changed Pa_StopStream() so that it waits for the buffers to drain.</li>
<li>
Added Pa_AbortStream() that stops immediately without waiting.</li>
<li>
Added new test: patest_stop.c to test above two mods.</li>
<li>
Fixed Pa_StreamTime() so that it returns current play position instead
of the write position. Added "patest_sync.c" to demo audio/video sync.</li>
<li>
Improved stability of Macintosh implementation. Added timeouts to prevent
hangs.</li>
<li>
Added Pa_GetSampleSize( PaSampleFormat format );</li>
<li>
Changes some "int"s to "long"s so that PA works properly on Macintosh which
often compiles using 16 bit ints.</li>
<li>
Added Implementation Guide</li>
</ul>
</blockquote>
<h2>
<b>V12 - 1/9/01</b></h2>
<blockquote>
<ul>
<li>
Mac now scans for and queries all devices. But it does not yet support
selecting any other than the default device.</li>
<li>
Blocking I/O calls renamed to separate them from the PortAudio API.</li>
<li>
Cleaned up indentation problems with tabs versus spaces.</li>
<li>
Now attempts to correct bogus sample rate info returned from DirectSound
device queries.</li>
</ul>
</blockquote>
</body>
</html>

View file

@ -0,0 +1,19 @@
rem Use Astyle to fix style in 'C' files
cd %1%
fixlines -p *.c
fixlines -p *.cpp
fixlines -p *.cc
astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.c
astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cpp
astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cc
del *.orig
@rem convert line terminators to Unix style LFs
fixlines -u *.c
fixlines -u *.cpp
fixlines -u *.cc
fixlines -u *.h
del *.bak
cd ..\

View file

@ -0,0 +1,7 @@
rem Use Astyle to fix style in a file
fixlines -p %1%
astyle --style=ansi -c -o --convert-tabs --indent-preprocessor %1%
del %1%.orig
@rem convert line terminators to Unix style LFs
fixlines -u %1%
del %1%.bak

View file

@ -0,0 +1,89 @@
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.79 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Phil Burk">
<meta name="Description" content="PortAudio is a cross platform, open-source, audio I/O library.It provides a very simple API for recording and/or playing sound using a simple callback function.">
<meta name="KeyWords" content="audio, library, portable, open-source, DirectSound,sound, music, JSyn, synthesis,">
<title>PortAudio Implementations for DirectSound</title>
</head>
<body>
&nbsp;
<center><table COLS=1 WIDTH="100%" BGCOLOR="#FADA7A" >
<tr>
<td>
<center>
<h1>
PortAudio - Portable Audio Library</h1></center>
</td>
</tr>
</table></center>
<p>Last updated 5/6/02.
<p>PortAudio is a cross platform, <a href="#License">open-source</a>, audio
I/O library proposed by <b>Ross Bencina</b> to the <a href="http://shoko.calarts.edu/~glmrboy/musicdsp/music-dsp.html">music-dsp</a>
mailing list. It lets you write simple audio programs in 'C' that will
compile and run on <b>Windows, Macintosh, Unix, BeOS</b>. PortAudio is
intended to promote the exchange of audio synthesis software between developers
on different platforms.
<p>For complete information on PortAudio and to download the latest releases,
please visit "<b><font size=+2><a href="http://www.portaudio.com">http://www.portaudio.com</a></font></b>".
<br>&nbsp;
<br>&nbsp;
<center>
<h2>
<b><a href="docs/index.html">Click here for Documentation</a></b></h2></center>
<h2>
<b><font size=+2></font></b></h2>
<h2>
<b><font size=+2>Contacts and E-Mail List</font></b></h2>
<ul>
<li>
If you are using or implementing PortAudio then please join the <b><font size=+1><a href="http://techweb.rfa.org/mailman/listinfo/portaudio">PortAudio
mail list</a></font><font size=+2> </font></b>generously administered by
<b>Bill
Eldridge</b>.</li>
<li>
If you find bugs in one of these implementations, or have suggestions,
please e-mail them to <a href="mailto:philburk@softsynth.com">Phil Burk</a>.</li>
<li>
If you make improvements to the library, please send them to us so we can
incorporate the improvements.</li>
</ul>
<h2>
<a NAME="License"></a>License</h2>
PortAudio Portable Real-Time Audio Library
<br>Copyright (c) 1999-2000 Ross Bencina and Phil Burk
<p>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:
<ul>
<li>
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.</li>
<li>
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.</li>
</ul>
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 ON INFRINGEMENT.
<br>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.
<br>&nbsp;
</body>
</html>

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
There is a bug in the ASIO SDK that causes the Macintosh version to often fail during initialization. Here is a patch that you can apply.
In codefragments.cpp replace getFrontProcessDirectory function with
the following one (GetFrontProcess replaced by GetCurrentProcess)
bool CodeFragments::getFrontProcessDirectory(void *specs)
{
FSSpec *fss = (FSSpec *)specs;
ProcessInfoRec pif;
ProcessSerialNumber psn;
memset(&psn,0,(long)sizeof(ProcessSerialNumber));
// if(GetFrontProcess(&psn) == noErr) // wrong !!!
if(GetCurrentProcess(&psn) == noErr) // correct !!!
{
pif.processName = 0;
pif.processAppSpec = fss;
pif.processInfoLength = sizeof(ProcessInfoRec);
if(GetProcessInformation(&psn, &pif) == noErr)
return true;
}
return false;
}

View file

@ -0,0 +1,538 @@
/*
* $Id$
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* BeOS Media Kit Implementation by Joshua Haberman
*
* Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
*
* 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.
*
* ---
*
* Significant portions of this file are based on sample code from Be. The
* Be Sample Code Licence follows:
*
* Copyright 1991-1999, Be Incorporated.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions, and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <be/media/BufferGroup.h>
#include <be/media/Buffer.h>
#include <be/media/TimeSource.h>
#include "PlaybackNode.h"
#define PRINT(x) { printf x; fflush(stdout); }
#ifdef DEBUG
#define DBUG(x) PRINT(x)
#else
#define DBUG(x)
#endif
PaPlaybackNode::PaPlaybackNode(uint32 channels, float frame_rate, uint32 frames_per_buffer,
PortAudioCallback* callback, void *user_data) :
BMediaNode("PortAudio input node"),
BBufferProducer(B_MEDIA_RAW_AUDIO),
BMediaEventLooper(),
mAborted(false),
mRunning(false),
mBufferGroup(NULL),
mDownstreamLatency(0),
mStartTime(0),
mCallback(callback),
mUserData(user_data),
mFramesPerBuffer(frames_per_buffer)
{
DBUG(("Constructor called.\n"));
mPreferredFormat.type = B_MEDIA_RAW_AUDIO;
mPreferredFormat.u.raw_audio.channel_count = channels;
mPreferredFormat.u.raw_audio.frame_rate = frame_rate;
mPreferredFormat.u.raw_audio.byte_order =
(B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN;
mPreferredFormat.u.raw_audio.buffer_size =
media_raw_audio_format::wildcard.buffer_size;
mOutput.destination = media_destination::null;
mOutput.format = mPreferredFormat;
/* The amount of time it takes for this node to produce a buffer when
* asked. Essentially, it is how long the user's callback takes to run.
* We set this to be the length of the sound data each buffer of the
* requested size can hold. */
//mInternalLatency = (bigtime_t)(1000000 * frames_per_buffer / frame_rate);
/* ACK! it seems that the mixer (at least on my machine) demands that IT
* specify the buffer size, so for now I'll just make a generic guess here */
mInternalLatency = 1000000 / 20;
}
PaPlaybackNode::~PaPlaybackNode()
{
DBUG(("Destructor called.\n"));
Quit(); /* Stop the BMediaEventLooper thread */
}
/*************************
*
* Local methods
*
*/
bool PaPlaybackNode::IsRunning()
{
return mRunning;
}
PaTimestamp PaPlaybackNode::GetStreamTime()
{
BTimeSource *timeSource = TimeSource();
PaTimestamp time = (timeSource->Now() - mStartTime) *
mPreferredFormat.u.raw_audio.frame_rate / 1000000;
return time;
}
void PaPlaybackNode::SetSampleFormat(PaSampleFormat inFormat,
PaSampleFormat outFormat)
{
uint32 beOutFormat;
switch(outFormat)
{
case paFloat32:
beOutFormat = media_raw_audio_format::B_AUDIO_FLOAT;
mOutputSampleWidth = 4;
break;
case paInt16:
beOutFormat = media_raw_audio_format::B_AUDIO_SHORT;
mOutputSampleWidth = 2;
break;
case paInt32:
beOutFormat = media_raw_audio_format::B_AUDIO_INT;
mOutputSampleWidth = 4;
break;
case paInt8:
beOutFormat = media_raw_audio_format::B_AUDIO_CHAR;
mOutputSampleWidth = 1;
break;
case paUInt8:
beOutFormat = media_raw_audio_format::B_AUDIO_UCHAR;
mOutputSampleWidth = 1;
break;
case paInt24:
case paPackedInt24:
case paCustomFormat:
DBUG(("Unsupported output format: %x\n", outFormat));
break;
default:
DBUG(("Unknown output format: %x\n", outFormat));
}
mPreferredFormat.u.raw_audio.format = beOutFormat;
mFramesPerBuffer * mPreferredFormat.u.raw_audio.channel_count * mOutputSampleWidth;
}
BBuffer *PaPlaybackNode::FillNextBuffer(bigtime_t time)
{
/* Get a buffer from the buffer group */
BBuffer *buf = mBufferGroup->RequestBuffer(
mOutput.format.u.raw_audio.buffer_size, BufferDuration());
unsigned long frames = mOutput.format.u.raw_audio.buffer_size /
mOutputSampleWidth / mOutput.format.u.raw_audio.channel_count;
bigtime_t start_time;
int ret;
if( !buf )
{
DBUG(("Unable to allocate a buffer\n"));
return NULL;
}
start_time = mStartTime +
(bigtime_t)((double)mSamplesSent /
(double)mOutput.format.u.raw_audio.frame_rate /
(double)mOutput.format.u.raw_audio.channel_count *
1000000.0);
/* Now call the user callback to get the data */
ret = mCallback(NULL, /* Input buffer */
buf->Data(), /* Output buffer */
frames, /* Frames per buffer */
mSamplesSent / mOutput.format.u.raw_audio.channel_count, /* timestamp */
mUserData);
if( ret )
mAborted = true;
media_header *hdr = buf->Header();
hdr->type = B_MEDIA_RAW_AUDIO;
hdr->size_used = mOutput.format.u.raw_audio.buffer_size;
hdr->time_source = TimeSource()->ID();
hdr->start_time = start_time;
return buf;
}
/*************************
*
* BMediaNode methods
*
*/
BMediaAddOn *PaPlaybackNode::AddOn( int32 * ) const
{
DBUG(("AddOn() called.\n"));
return NULL; /* we don't provide service to outside applications */
}
status_t PaPlaybackNode::HandleMessage( int32 message, const void *data,
size_t size )
{
DBUG(("HandleMessage() called.\n"));
return B_ERROR; /* we don't define any custom messages */
}
/*************************
*
* BMediaEventLooper methods
*
*/
void PaPlaybackNode::NodeRegistered()
{
DBUG(("NodeRegistered() called.\n"));
/* Start the BMediaEventLooper thread */
SetPriority(B_REAL_TIME_PRIORITY);
Run();
/* set up as much information about our output as we can */
mOutput.source.port = ControlPort();
mOutput.source.id = 0;
mOutput.node = Node();
::strcpy(mOutput.name, "PortAudio Playback");
}
void PaPlaybackNode::HandleEvent( const media_timed_event *event,
bigtime_t lateness, bool realTimeEvent )
{
// DBUG(("HandleEvent() called.\n"));
status_t err;
switch(event->type)
{
case BTimedEventQueue::B_START:
DBUG((" Handling a B_START event\n"));
if( RunState() != B_STARTED )
{
mStartTime = event->event_time + EventLatency();
mSamplesSent = 0;
mAborted = false;
mRunning = true;
media_timed_event firstEvent( mStartTime,
BTimedEventQueue::B_HANDLE_BUFFER );
EventQueue()->AddEvent( firstEvent );
}
break;
case BTimedEventQueue::B_STOP:
DBUG((" Handling a B_STOP event\n"));
mRunning = false;
EventQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true,
BTimedEventQueue::B_HANDLE_BUFFER );
break;
case BTimedEventQueue::B_HANDLE_BUFFER:
//DBUG((" Handling a B_HANDLE_BUFFER event\n"));
/* make sure we're started and connected */
if( RunState() != BMediaEventLooper::B_STARTED ||
mOutput.destination == media_destination::null )
break;
BBuffer *buffer = FillNextBuffer(event->event_time);
/* make sure we weren't aborted while this routine was running.
* this can happen in one of two ways: either the callback returned
* nonzero (in which case mAborted is set in FillNextBuffer() ) or
* the client called AbortStream */
if( mAborted )
{
if( buffer )
buffer->Recycle();
Stop(0, true);
break;
}
if( buffer )
{
err = SendBuffer(buffer, mOutput.destination);
if( err != B_OK )
buffer->Recycle();
}
mSamplesSent += mOutput.format.u.raw_audio.buffer_size / mOutputSampleWidth;
/* Now schedule the next buffer event, so we can send another
* buffer when this one runs out. We calculate when it should
* happen by calculating when the data we just sent will finish
* playing.
*
* NOTE, however, that the event will actually get generated
* earlier than we specify, to account for the latency it will
* take to produce the buffer. It uses the latency value we
* specified in SetEventLatency() to determine just how early
* to generate it. */
/* totalPerformanceTime includes the time represented by the buffer
* we just sent */
bigtime_t totalPerformanceTime = (bigtime_t)((double)mSamplesSent /
(double)mOutput.format.u.raw_audio.channel_count /
(double)mOutput.format.u.raw_audio.frame_rate * 1000000.0);
bigtime_t nextEventTime = mStartTime + totalPerformanceTime;
media_timed_event nextBufferEvent(nextEventTime,
BTimedEventQueue::B_HANDLE_BUFFER);
EventQueue()->AddEvent(nextBufferEvent);
break;
}
}
/*************************
*
* BBufferProducer methods
*
*/
status_t PaPlaybackNode::FormatSuggestionRequested( media_type type,
int32 /*quality*/, media_format* format )
{
/* the caller wants to know this node's preferred format and provides
* a suggestion, asking if we support it */
DBUG(("FormatSuggestionRequested() called.\n"));
if(!format)
return B_BAD_VALUE;
*format = mPreferredFormat;
/* we only support raw audio (a wildcard is okay too) */
if ( type == B_MEDIA_UNKNOWN_TYPE || type == B_MEDIA_RAW_AUDIO )
return B_OK;
else
return B_MEDIA_BAD_FORMAT;
}
status_t PaPlaybackNode::FormatProposal( const media_source& output,
media_format* format )
{
/* This is similar to FormatSuggestionRequested(), but it is actually part
* of the negotiation process. We're given the opportunity to specify any
* properties that are wildcards (ie. properties that the other node doesn't
* care one way or another about) */
DBUG(("FormatProposal() called.\n"));
/* Make sure this proposal really applies to our output */
if( output != mOutput.source )
return B_MEDIA_BAD_SOURCE;
/* We return two things: whether we support the proposed format, and our own
* preferred format */
*format = mPreferredFormat;
if( format->type == B_MEDIA_UNKNOWN_TYPE || format->type == B_MEDIA_RAW_AUDIO )
return B_OK;
else
return B_MEDIA_BAD_FORMAT;
}
status_t PaPlaybackNode::FormatChangeRequested( const media_source& source,
const media_destination& destination, media_format* io_format, int32* )
{
/* we refuse to change formats, supporting only 1 */
DBUG(("FormatChangeRequested() called.\n"));
return B_ERROR;
}
status_t PaPlaybackNode::GetNextOutput( int32* cookie, media_output* out_output )
{
/* this is where we allow other to enumerate our outputs -- the cookie is
* an integer we can use to keep track of where we are in enumeration. */
DBUG(("GetNextOutput() called.\n"));
if( *cookie == 0 )
{
*out_output = mOutput;
*cookie = 1;
return B_OK;
}
return B_BAD_INDEX;
}
status_t PaPlaybackNode::DisposeOutputCookie( int32 cookie )
{
DBUG(("DisposeOutputCookie() called.\n"));
return B_OK;
}
void PaPlaybackNode::LateNoticeReceived( const media_source& what,
bigtime_t how_much, bigtime_t performance_time )
{
/* This function is called as notification that a buffer we sent wasn't
* received by the time we stamped it with -- it got there late. Basically,
* it means we underestimated our own latency, so we should increase it */
DBUG(("LateNoticeReceived() called.\n"));
if( what != mOutput.source )
return;
if( RunMode() == B_INCREASE_LATENCY )
{
mInternalLatency += how_much;
SetEventLatency( mDownstreamLatency + mInternalLatency );
DBUG(("Increasing latency to %Ld\n", mDownstreamLatency + mInternalLatency));
}
else
DBUG(("I don't know what to do with this notice!"));
}
void PaPlaybackNode::EnableOutput( const media_source& what, bool enabled,
int32* )
{
DBUG(("EnableOutput() called.\n"));
/* stub -- we don't support this yet */
}
status_t PaPlaybackNode::PrepareToConnect( const media_source& what,
const media_destination& where, media_format* format,
media_source* out_source, char* out_name )
{
/* the final stage of format negotiations. here we _must_ make specific any
* remaining wildcards */
DBUG(("PrepareToConnect() called.\n"));
/* make sure this really refers to our source */
if( what != mOutput.source )
return B_MEDIA_BAD_SOURCE;
/* make sure we're not already connected */
if( mOutput.destination != media_destination::null )
return B_MEDIA_ALREADY_CONNECTED;
if( format->type != B_MEDIA_RAW_AUDIO )
return B_MEDIA_BAD_FORMAT;
if( format->u.raw_audio.format != mPreferredFormat.u.raw_audio.format )
return B_MEDIA_BAD_FORMAT;
if( format->u.raw_audio.buffer_size ==
media_raw_audio_format::wildcard.buffer_size )
{
DBUG(("We were left to decide buffer size: choosing 2048"));
format->u.raw_audio.buffer_size = 2048;
}
else
DBUG(("Using consumer specified buffer size of %lu.\n",
format->u.raw_audio.buffer_size));
/* Reserve the connection, return the information */
mOutput.destination = where;
mOutput.format = *format;
*out_source = mOutput.source;
strncpy( out_name, mOutput.name, B_MEDIA_NAME_LENGTH );
return B_OK;
}
void PaPlaybackNode::Connect(status_t error, const media_source& source,
const media_destination& destination, const media_format& format, char* io_name)
{
DBUG(("Connect() called.\n"));

View file

@ -0,0 +1,108 @@
/*
* $Id$
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* BeOS Media Kit Implementation by Joshua Haberman
*
* Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
*
* 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 <be/media/MediaRoster.h>
#include <be/media/MediaEventLooper.h>
#include <be/media/BufferProducer.h>
#include "portaudio.h"
class PaPlaybackNode :
public BBufferProducer,
public BMediaEventLooper
{
public:
PaPlaybackNode( uint32 channels, float frame_rate, uint32 frames_per_buffer,
PortAudioCallback *callback, void *user_data );
~PaPlaybackNode();
/* Local methods ******************************************/
BBuffer *FillNextBuffer(bigtime_t time);
void SetSampleFormat(PaSampleFormat inFormat, PaSampleFormat outFormat);
bool IsRunning();
PaTimestamp GetStreamTime();
/* BMediaNode methods *************************************/
BMediaAddOn* AddOn( int32 * ) const;
status_t HandleMessage( int32 message, const void *data, size_t size );
/* BMediaEventLooper methods ******************************/
void HandleEvent( const media_timed_event *event, bigtime_t lateness,
bool realTimeEvent );
void NodeRegistered();
/* BBufferProducer methods ********************************/
status_t FormatSuggestionRequested( media_type type, int32 quality,
media_format* format );
status_t FormatProposal( const media_source& output, media_format* format );
status_t FormatChangeRequested( const media_source& source,
const media_destination& destination, media_format* io_format, int32* );
status_t GetNextOutput( int32* cookie, media_output* out_output );
status_t DisposeOutputCookie( int32 cookie );
void LateNoticeReceived( const media_source& what, bigtime_t how_much,
bigtime_t performance_time );
void EnableOutput( const media_source& what, bool enabled, int32* _deprecated_ );
status_t PrepareToConnect( const media_source& what,
const media_destination& where, media_format* format,
media_source* out_source, char* out_name );
void Connect(status_t error, const media_source& source,
const media_destination& destination, const media_format& format,
char* io_name);
void Disconnect(const media_source& what, const media_destination& where);
status_t SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup);
bool mAborted;
private:
media_output mOutput;
media_format mPreferredFormat;
uint32 mOutputSampleWidth, mFramesPerBuffer;
BBufferGroup *mBufferGroup;
bigtime_t mDownstreamLatency, mInternalLatency, mStartTime;
uint64 mSamplesSent;
PortAudioCallback *mCallback;
void *mUserData;
bool mRunning;
};

View file

@ -0,0 +1,441 @@
/*
* $Id$
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* BeOS Media Kit Implementation by Joshua Haberman
*
* Copyright (c) 2001 Joshua Haberman <joshua@haberman.com>
*
* 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 <be/app/Application.h>
#include <be/kernel/OS.h>
#include <be/media/RealtimeAlloc.h>
#include <be/media/MediaRoster.h>
#include <be/media/TimeSource.h>
#include <stdio.h>
#include <string.h>
#include "portaudio.h"
#include "pa_host.h"
#include "PlaybackNode.h"
#define PRINT(x) { printf x; fflush(stdout); }
#ifdef DEBUG
#define DBUG(x) PRINT(x)
#else
#define DBUG(x)
#endif
typedef struct PaHostSoundControl
{
/* These members are common to all modes of operation */
media_node pahsc_TimeSource; /* the sound card's DAC. */
media_format pahsc_Format;
/* These methods are specific to playing mode */
media_node pahsc_OutputNode; /* output to the mixer */
media_node pahsc_InputNode; /* reads data from user callback -- PA specific */
media_input pahsc_MixerInput; /* input jack on the soundcard's mixer. */
media_output pahsc_PaOutput; /* output jack from the PA node */
PaPlaybackNode *pahsc_InputNodeInstance;
}
PaHostSoundControl;
/*************************************************************************/
PaDeviceID Pa_GetDefaultOutputDeviceID( void )
{
/* stub */
return 0;
}
/*************************************************************************/
PaDeviceID Pa_GetDefaultInputDeviceID( void )
{
/* stub */
return 0;
}
/*************************************************************************/
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id )
{
/* stub */
return NULL;
}
/*************************************************************************/
int Pa_CountDevices()
{
/* stub */
return 1;
}
/*************************************************************************/
PaError PaHost_Init( void )
{
/* we have to create this in order to use BMediaRoster. I hope it doesn't
* cause problems */
be_app = new BApplication("application/x-vnd.portaudio-app");
return paNoError;
}
PaError PaHost_Term( void )
{
delete be_app;
return paNoError;
}
/*************************************************************************/
PaError PaHost_StreamActive( internalPortAudioStream *past )
{
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
DBUG(("IsRunning returning: %s\n",
pahsc->pahsc_InputNodeInstance->IsRunning() ? "true" : "false"));
return (PaError)pahsc->pahsc_InputNodeInstance->IsRunning();
}
PaError PaHost_StartOutput( internalPortAudioStream *past )
{
return paNoError;
}
/*************************************************************************/
PaError PaHost_StartInput( internalPortAudioStream *past )
{
return paNoError;
}
/*************************************************************************/
PaError PaHost_StopInput( internalPortAudioStream *past, int abort )
{
return paNoError;
}
/*************************************************************************/
PaError PaHost_StopOutput( internalPortAudioStream *past, int abort )
{
return paNoError;
}
/*************************************************************************/
PaError PaHost_StartEngine( internalPortAudioStream *past )
{
bigtime_t very_soon, start_latency;
status_t err;
BMediaRoster *roster = BMediaRoster::Roster(&err);
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
/* for some reason, err indicates an error (though nothing it wrong)
* when the DBUG macro in pa_lib.c is enabled. It's reproducably
* linked. Weird. */
if( !roster /* || err != B_OK */ )
{
DBUG(("No media server! err=%d, roster=%x\n", err, roster));
return paHostError;
}
/* tell the node when to start -- since there aren't any other nodes
* starting that we have to wait for, just tell it to start now
*/
BTimeSource *timeSource = roster->MakeTimeSourceFor(pahsc->pahsc_TimeSource);
very_soon = timeSource->PerformanceTimeFor( BTimeSource::RealTime() );
timeSource->Release();
/* Add the latency of starting the network of nodes */
err = roster->GetStartLatencyFor( pahsc->pahsc_TimeSource, &start_latency );
very_soon += start_latency;
err = roster->StartNode( pahsc->pahsc_InputNode, very_soon );
/* No need to start the mixer -- it's always running */
return paNoError;
}
/*************************************************************************/
PaError PaHost_StopEngine( internalPortAudioStream *past, int abort )
{
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
BMediaRoster *roster = BMediaRoster::Roster();
if( !roster )
{
DBUG(("No media roster!\n"));
return paHostError;
}
if( !pahsc )
return paHostError;
/* this crashes, and I don't know why yet */
// if( abort )
// pahsc->pahsc_InputNodeInstance->mAborted = true;
roster->StopNode(pahsc->pahsc_InputNode, 0, /* immediate = */ true);
return paNoError;
}
/*************************************************************************/
PaError PaHost_OpenStream( internalPortAudioStream *past )
{
status_t err;
BMediaRoster *roster = BMediaRoster::Roster(&err);
PaHostSoundControl *pahsc;
/* Allocate and initialize host data. */
pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
if( pahsc == NULL )
{
goto error;
}
memset( pahsc, 0, sizeof(PaHostSoundControl) );
past->past_DeviceData = (void *) pahsc;
if( !roster /* || err != B_OK */ )
{
/* no media server! */
DBUG(("No media server.\n"));
goto error;
}
if ( past->past_NumInputChannels > 0 && past->past_NumOutputChannels > 0 )
{
/* filter -- not implemented yet */
goto error;
}
else if ( past->past_NumInputChannels > 0 )
{
/* recorder -- not implemented yet */
goto error;
}
else
{
/* player ****************************************************************/
status_t err;
int32 num;
/* First we need to create the three components (like components in a stereo
* system). The mixer component is our interface to the sound card, data
* we write there will get played. The BePA_InputNode component is the node
* which represents communication with the PA client (it is what calls the
* client's callbacks). The time source component is the sound card's DAC,
* which allows us to slave the other components to it instead of the system
* clock. */
err = roster->GetAudioMixer( &pahsc->pahsc_OutputNode );
if( err != B_OK )
{
DBUG(("Couldn't get default mixer.\n"));
goto error;
}
err = roster->GetTimeSource( &pahsc->pahsc_TimeSource );
if( err != B_OK )
{
DBUG(("Couldn't get time source.\n"));
goto error;
}
pahsc->pahsc_InputNodeInstance = new PaPlaybackNode(2, 44100,
past->past_FramesPerUserBuffer, past->past_Callback, past->past_UserData );
pahsc->pahsc_InputNodeInstance->SetSampleFormat(0,
past->past_OutputSampleFormat);
err = roster->RegisterNode( pahsc->pahsc_InputNodeInstance );
if( err != B_OK )
{
DBUG(("Unable to register node.\n"));
goto error;
}
roster->GetNodeFor( pahsc->pahsc_InputNodeInstance->Node().node,
&pahsc->pahsc_InputNode );
if( err != B_OK )
{
DBUG(("Unable to get input node.\n"));
goto error;
}
/* Now we have three components (nodes) sitting next to each other. The
* next step is to look at them and find their inputs and outputs so we can
* wire them together. */
err = roster->GetFreeInputsFor( pahsc->pahsc_OutputNode,
&pahsc->pahsc_MixerInput, 1, &num, B_MEDIA_RAW_AUDIO );
if( err != B_OK || num < 1 )
{
DBUG(("Couldn't get the mixer input.\n"));
goto error;
}
err = roster->GetFreeOutputsFor( pahsc->pahsc_InputNode,
&pahsc->pahsc_PaOutput, 1, &num, B_MEDIA_RAW_AUDIO );
if( err != B_OK || num < 1 )
{
DBUG(("Couldn't get PortAudio output.\n"));
goto error;
}
/* We've found the input and output -- the final step is to run a wire
* between them so they are connected. */
/* try to make the mixer input adapt to what PA sends it */
pahsc->pahsc_Format = pahsc->pahsc_PaOutput.format;
roster->Connect( pahsc->pahsc_PaOutput.source,
pahsc->pahsc_MixerInput.destination, &pahsc->pahsc_Format,
&pahsc->pahsc_PaOutput, &pahsc->pahsc_MixerInput );
/* Actually, there's one final step -- tell them all to sync to the
* sound card's DAC */
roster->SetTimeSourceFor( pahsc->pahsc_InputNode.node,
pahsc->pahsc_TimeSource.node );
roster->SetTimeSourceFor( pahsc->pahsc_OutputNode.node,
pahsc->pahsc_TimeSource.node );
}
return paNoError;
error:
PaHost_CloseStream( past );
return paHostError;
}
/*************************************************************************/
PaError PaHost_CloseStream( internalPortAudioStream *past )
{
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
status_t err;
BMediaRoster *roster = BMediaRoster::Roster(&err);
if( !roster )
{
DBUG(("Couldn't get media roster\n"));
return paHostError;
}
if( !pahsc )
return paHostError;
/* Disconnect all the connections we made when opening the stream */
roster->Disconnect(pahsc->pahsc_InputNode.node, pahsc->pahsc_PaOutput.source,
pahsc->pahsc_OutputNode.node, pahsc->pahsc_MixerInput.destination);
DBUG(("Calling ReleaseNode()"));
roster->ReleaseNode(pahsc->pahsc_InputNode);
/* deleting the node shouldn't be necessary -- it is reference counted, and will
* delete itself when its references drop to zero. the call to ReleaseNode()
* above should decrease its reference count */
pahsc->pahsc_InputNodeInstance = NULL;
return paNoError;
}
/*************************************************************************/
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
{
internalPortAudioStream *past = (internalPortAudioStream *) stream;
PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData;
return pahsc->pahsc_InputNodeInstance->GetStreamTime();
}
/*************************************************************************/
void Pa_Sleep( long msec )
{
/* snooze() takes microseconds */
snooze( msec * 1000 );
}
/*************************************************************************
* Allocate memory that can be accessed in real-time.
* This may need to be held in physical memory so that it is not
* paged to virtual memory.
* This call MUST be balanced with a call to PaHost_FreeFastMemory().
* Memory will be set to zero.
*/
void *PaHost_AllocateFastMemory( long numBytes )
{
/* BeOS supports non-pagable memory through pools -- a pool is an area
* of physical memory that is locked. It would be best to pre-allocate
* that pool and then hand out memory from it, but we don't know in
* advance how much we'll need. So for now, we'll allocate a pool
* for every request we get, storing a pointer to the pool at the
* beginning of the allocated memory */
rtm_pool *pool;
void *addr;
long size = numBytes + sizeof(rtm_pool *);
static int counter = 0;
char pool_name[100];
/* Every pool needs a unique name. */
sprintf(pool_name, "PaPoolNumber%d", counter++);
if( rtm_create_pool( &pool, size, pool_name ) != B_OK )
return 0;
addr = rtm_alloc( pool, size );
if( addr == NULL )
return 0;
memset( addr, 0, numBytes );
*((rtm_pool **)addr) = pool; // store the pointer to the pool
addr = (rtm_pool **)addr + 1; // and return the next location in memory
return addr;
}
/*************************************************************************
* Free memory that could be accessed in real-time.
* This call MUST be balanced with a call to PaHost_AllocateFastMemory().
*/
void PaHost_FreeFastMemory( void *addr, long numBytes )
{
rtm_pool *pool;
if( addr == NULL )
return;
addr = (rtm_pool **)addr - 1;
pool = *((rtm_pool **)addr);
rtm_free( addr );
rtm_delete_pool( pool );
}

View file

@ -0,0 +1,402 @@
/*
* pa_conversions.c
* portaudio
*
* Created by Phil Burk on Mon Mar 18 2002.
*
*/
#include <stdio.h>
#include "portaudio.h"
#include "pa_host.h"
#define CLIP( val, min, max ) { val = ((val) < (min)) ? min : (((val) < (max)) ? (max) : (val)); }
/*************************************************************************/
static void PaConvert_Float32_Int16(
float *sourceBuffer, int sourceStride,
short *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
short samp = (short) (*sourceBuffer * (32767.0f));
*targetBuffer = samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Float32_Int16_Clip(
float *sourceBuffer, int sourceStride,
short *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
long samp = (long) (*sourceBuffer * (32767.0f));
CLIP( samp, -0x8000, 0x7FFF );
*targetBuffer = (short) samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Float32_Int16_ClipDither(
float *sourceBuffer, int sourceStride,
short *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
// use smaller scaler to prevent overflow when we add the dither
float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
float dithered = (*sourceBuffer * (32766.0f)) + dither;
long samp = (long) dithered;
CLIP( samp, -0x8000, 0x7FFF );
*targetBuffer = (short) samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Float32_Int16_Dither(
float *sourceBuffer, int sourceStride,
short *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
// use smaller scaler to prevent overflow when we add the dither
float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
float dithered = (*sourceBuffer * (32766.0f)) + dither;
*targetBuffer = (short) dithered;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Int16_Float32(
short *sourceBuffer, int sourceStride,
float *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
float samp = *sourceBuffer * (1.0f / 32768.0f);
*targetBuffer = samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Float32_Int8(
float *sourceBuffer, int sourceStride,
char *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
char samp = (char) (*sourceBuffer * (127.0));
*targetBuffer = samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Float32_Int8_Clip(
float *sourceBuffer, int sourceStride,
char *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
long samp = *sourceBuffer * 127.0f;
CLIP( samp, -0x80, 0x7F );
*targetBuffer = (char) samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Float32_Int8_ClipDither(
float *sourceBuffer, int sourceStride,
char *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
// use smaller scaler to prevent overflow when we add the dither
float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE;
float dithered = (*sourceBuffer * (126.0f)) + dither;
long samp = (long) dithered;
CLIP( samp, -0x80, 0x7F );
*targetBuffer = (char) samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Float32_Int8_Dither(
float *sourceBuffer, int sourceStride,
char *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
// use smaller scaler to prevent overflow when we add the dither
float dither = PaConvert_TriangularDither() * PA_DITHER_SCALE; //FIXME
float dithered = (*sourceBuffer * (126.0f)) + dither;
long samp = (long) dithered;
*targetBuffer = (char) samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Int8_Float32(
char *sourceBuffer, int sourceStride,
float *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
float samp = *sourceBuffer * (1.0f / 128.0f);
*targetBuffer = samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_Float32_UInt8(
float *sourceBuffer, int sourceStride,
unsigned char *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
unsigned char samp = 128 + (unsigned char) (*sourceBuffer * (127.0));
*targetBuffer = samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static void PaConvert_UInt8_Float32(
unsigned char *sourceBuffer, int sourceStride,
float *targetBuffer, int targetStride,
int numSamples )
{
int i;
for( i=0; i<numSamples; i++ )
{
float samp = (*sourceBuffer - 128) * (1.0f / 128.0f);
*targetBuffer = samp;
sourceBuffer += sourceStride;
targetBuffer += targetStride;
}
}
/*************************************************************************/
static PortAudioConverter *PaConvert_SelectProc( PaSampleFormat sourceFormat,
PaSampleFormat targetFormat, int ifClip, int ifDither )
{
PortAudioConverter *proc = NULL;
switch( sourceFormat )
{
case paUInt8:
switch( targetFormat )
{
case paFloat32:
proc = (PortAudioConverter *) PaConvert_UInt8_Float32;
break;
default:
break;
}
break;
case paInt8:
switch( targetFormat )
{
case paFloat32:
proc = (PortAudioConverter *) PaConvert_Int8_Float32;
break;
default:
break;
}
break;
case paInt16:
switch( targetFormat )
{
case paFloat32:
proc = (PortAudioConverter *) PaConvert_Int16_Float32;
break;
default:
break;
}
break;
case paFloat32:
switch( targetFormat )
{
case paUInt8:
proc = (PortAudioConverter *) PaConvert_Float32_UInt8;
break;
case paInt8:
if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_ClipDither;
else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Clip;
else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int8_Dither;
else proc = (PortAudioConverter *) PaConvert_Float32_Int8;
break;
case paInt16:
if( ifClip && ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_ClipDither;
else if( ifClip ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Clip;
else if( ifDither ) proc = (PortAudioConverter *) PaConvert_Float32_Int16_Dither;
else proc = (PortAudioConverter *) PaConvert_Float32_Int16;
break;
default:
break;
}
break;
default:
break;
}
return proc;
}
/*************************************************************************/
PaError PaConvert_SetupInput( internalPortAudioStream *past,
PaSampleFormat nativeInputSampleFormat )
{
past->past_NativeInputSampleFormat = nativeInputSampleFormat;
past->past_InputConversionSourceStride = 1;
past->past_InputConversionTargetStride = 1;
if( nativeInputSampleFormat != past->past_InputSampleFormat )
{
int ifDither = (past->past_Flags & paDitherOff) == 0;
past->past_InputConversionProc = PaConvert_SelectProc( nativeInputSampleFormat,
past->past_InputSampleFormat, 0, ifDither );
if( past->past_InputConversionProc == NULL ) return paSampleFormatNotSupported;
}
else
{
past->past_InputConversionProc = NULL; /* no conversion necessary */
}
return paNoError;
}
/*************************************************************************/
PaError PaConvert_SetupOutput( internalPortAudioStream *past,
PaSampleFormat nativeOutputSampleFormat )
{
past->past_NativeOutputSampleFormat = nativeOutputSampleFormat;
past->past_OutputConversionSourceStride = 1;
past->past_OutputConversionTargetStride = 1;
if( nativeOutputSampleFormat != past->past_OutputSampleFormat )
{
int ifDither = (past->past_Flags & paDitherOff) == 0;
int ifClip = (past->past_Flags & paClipOff) == 0;
past->past_OutputConversionProc = PaConvert_SelectProc( past->past_OutputSampleFormat,
nativeOutputSampleFormat, ifClip, ifDither );
if( past->past_OutputConversionProc == NULL ) return paSampleFormatNotSupported;
}
else
{
past->past_OutputConversionProc = NULL; /* no conversion necessary */
}
return paNoError;
}
/*************************************************************************
** Called by host code.
** Convert input from native format to user format,
** call user code,
** then convert output to native format.
** Returns result from user callback.
*/
long PaConvert_Process( internalPortAudioStream *past,
void *nativeInputBuffer,
void *nativeOutputBuffer )
{
int userResult;
void *inputBuffer = NULL;
void *outputBuffer = NULL;
/* Get native input data. */
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
{
if( past->past_InputSampleFormat == past->past_NativeInputSampleFormat )
{
/* Already in native format so just read directly from native buffer. */
inputBuffer = nativeInputBuffer;
}
else
{
inputBuffer = past->past_InputBuffer;
/* Convert input data to user format. */
(*past->past_InputConversionProc)(nativeInputBuffer, past->past_InputConversionSourceStride,
inputBuffer, past->past_InputConversionTargetStride,
past->past_FramesPerUserBuffer * past->past_NumInputChannels );
}
}
/* Are we doing output? */
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
{
outputBuffer = (past->past_OutputConversionProc == NULL) ?
nativeOutputBuffer : past->past_OutputBuffer;
}
/*
AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
*/
/* Call user callback routine. */
userResult = past->past_Callback(
inputBuffer,
outputBuffer,
past->past_FramesPerUserBuffer,
past->past_FrameCount,
past->past_UserData );
/* Advance frame counter for timestamp. */
past->past_FrameCount += past->past_FramesPerUserBuffer; // FIXME - should this be in here?
/* Convert to native format if necessary. */
if( (past->past_OutputConversionProc != NULL ) && (outputBuffer != NULL) )
{
(*past->past_OutputConversionProc)( outputBuffer, past->past_OutputConversionSourceStride,
nativeOutputBuffer, past->past_OutputConversionTargetStride,
past->past_FramesPerUserBuffer * past->past_NumOutputChannels );
}
return userResult;
}

View file

@ -0,0 +1,185 @@
#ifndef PA_HOST_H
#define PA_HOST_H
/*
* $Id$
* Host dependant internal API for PortAudio
*
* Author: Phil Burk <philburk@softsynth.com>
*
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.softsynth.com/portaudio/
* DirectSound and Macintosh Implementation
* 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.
*
*/
#include "portaudio.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#ifndef SUPPORT_AUDIO_CAPTURE
#define SUPPORT_AUDIO_CAPTURE (1)
#endif
#ifndef int32
typedef long int32;
#endif
#ifndef uint32
typedef unsigned long uint32;
#endif
#ifndef int16
typedef short int16;
#endif
#ifndef uint16
typedef unsigned short uint16;
#endif
/* Used to convert between various sample formats. */
typedef void (PortAudioConverter)(
void *inputBuffer, int inputStride,
void *outputBuffer, int outputStride,
int numSamples );
#define PA_MAGIC (0x18273645)
/************************************************************************************/
/****************** Structures ******************************************************/
/************************************************************************************/
typedef struct internalPortAudioStream
{
uint32 past_Magic; /* ID for struct to catch bugs. */
/* User specified information. */
uint32 past_FramesPerUserBuffer;
uint32 past_NumUserBuffers;
double past_SampleRate; /* Closest supported sample rate. */
int past_NumInputChannels;
int past_NumOutputChannels;
PaDeviceID past_InputDeviceID;
PaDeviceID past_OutputDeviceID;
PaSampleFormat past_NativeInputSampleFormat;
PaSampleFormat past_InputSampleFormat;
PaSampleFormat past_NativeOutputSampleFormat;
PaSampleFormat past_OutputSampleFormat;
void *past_DeviceData;
PortAudioCallback *past_Callback;
void *past_UserData;
uint32 past_Flags;
/* Flags for communicating between foreground and background. */
volatile int past_IsActive; /* Background is still playing. */
volatile int past_StopSoon; /* Background should keep playing when buffers empty. */
volatile int past_StopNow; /* Background should stop playing now. */
/* These buffers are used when the native format does not match the user format. */
void *past_InputBuffer;
uint32 past_InputBufferSize;
void *past_OutputBuffer;
uint32 past_OutputBufferSize;
/* Measurements */
uint32 past_NumCallbacks;
PaTimestamp past_FrameCount; /* Frames output to buffer. */
/* For measuring CPU utilization. */
double past_AverageInsideCount;
double past_AverageTotalCount;
double past_Usage;
int past_IfLastExitValid;
/* Format Conversion */
/* These are setup by PaConversion_Setup() */
PortAudioConverter *past_InputConversionProc;
int past_InputConversionSourceStride;
int past_InputConversionTargetStride;
PortAudioConverter *past_OutputConversionProc;
int past_OutputConversionSourceStride;
int past_OutputConversionTargetStride;
}
internalPortAudioStream;
/************************************************************************************/
/******** These functions must be provided by a platform implementation. ************/
/************************************************************************************/
PaError PaHost_Init( void );
PaError PaHost_Term( void );
PaError PaHost_OpenStream( internalPortAudioStream *past );
PaError PaHost_CloseStream( internalPortAudioStream *past );
PaError PaHost_StartOutput( internalPortAudioStream *past );
PaError PaHost_StopOutput( internalPortAudioStream *past, int abort );
PaError PaHost_StartInput( internalPortAudioStream *past );
PaError PaHost_StopInput( internalPortAudioStream *past, int abort );
PaError PaHost_StartEngine( internalPortAudioStream *past );
PaError PaHost_StopEngine( internalPortAudioStream *past, int abort );
PaError PaHost_StreamActive( internalPortAudioStream *past );
void *PaHost_AllocateFastMemory( long numBytes );
void PaHost_FreeFastMemory( void *addr, long numBytes );
/* This only called if PA_VALIDATE_RATE IS CALLED. */
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
double *closestFrameRatePtr );
/**********************************************************************/
/************ Common Utility Routines provided by PA ******************/
/**********************************************************************/
/* PaHost_IsInitialized() returns non-zero if PA is initialized, 0 otherwise */
int PaHost_IsInitialized( void );
internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream );
int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable,
int numRates, double frameRate );
long Pa_CallConvertInt16( internalPortAudioStream *past,
short *nativeInputBuffer,
short *nativeOutputBuffer );
/* Calculate 2 LSB dither signal with a triangular distribution.
** Ranged properly for adding to a 32 bit 1.31 fixed point value prior to >>15.
** Range of output is +/- 65535
** Multiply by PA_DITHER_SCALE to get a float between -2.0 and 2.0. */
#define PA_DITHER_BITS (15)
#define PA_DITHER_SCALE (1.0f / ((1<<PA_DITHER_BITS)-1))
long PaConvert_TriangularDither( void );
PaError PaConvert_SetupInput( internalPortAudioStream *past,
PaSampleFormat nativeInputSampleFormat );
PaError PaConvert_SetupOutput( internalPortAudioStream *past,
PaSampleFormat nativeOutputSampleFormat );
long PaConvert_Process( internalPortAudioStream *past,
void *nativeInputBuffer,
void *nativeOutputBuffer );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_HOST_H */

View file

@ -0,0 +1,806 @@
/*
* $Id$
* Portable Audio I/O Library
* Host Independant Layer
*
* Based on the Open Source API proposed by Ross Bencina
* 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:
PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
PLB20010820 - fix dither and shift for recording PaUInt8 format
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
#ifdef _WIN32
#ifndef __MWERKS__
#include <memory.h>
#endif /* __MWERKS__ */
#else /* !_WIN32 */
#include <memory.h>
#endif /* _WIN32 */
#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"
/* The reason we might NOT want to validate the rate before opening the stream
* is because many DirectSound drivers lie about the rates they actually support.
*/
#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
/*
O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
*/
#ifndef FALSE
#define FALSE (0)
#define TRUE (!FALSE)
#endif
#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x) /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) */
static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
static PaError Pa_KillStream( PortAudioStream *stream, int abort );
/***********************************************************************/
int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
{
double err, minErr = allowableError;
int i, bestFit = -1;
for( i=0; i<numRates; i++ )
{
err = fabs( frameRate - rateTable[i] );
if( err < minErr )
{
minErr = err;
bestFit = i;
}
}
return bestFit;
}
/**************************************************************************
** Make sure sample rate is legal and also convert to enumeration for driver.
*/
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
double *closestFrameRatePtr )
{
long bestRateIndex;
const PaDeviceInfo *pdi;
pdi = Pa_GetDeviceInfo( id );
if( pdi == NULL )
{
return paInvalidDeviceId;
}
if( pdi->numSampleRates == -1 )
{
/* Is it out of range? */
if( (requestedFrameRate < pdi->sampleRates[0]) ||
(requestedFrameRate > pdi->sampleRates[1]) )
{
return paInvalidSampleRate;
}
*closestFrameRatePtr = requestedFrameRate;
}
else
{
bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
if( bestRateIndex < 0 ) return paInvalidSampleRate;
*closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
}
return paNoError;
}
/*************************************************************************/
PaError Pa_OpenStream(
PortAudioStream** streamPtrPtr,
PaDeviceID inputDeviceID,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDeviceID,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
unsigned long streamFlags,
PortAudioCallback *callback,
void *userData )
{
internalPortAudioStream *past = NULL;
PaError result = paNoError;
int bitsPerInputSample;
int bitsPerOutputSample;
/* Print passed parameters. */
DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
streamPtrPtr, inputDeviceID, numInputChannels,
inputSampleFormat, inputDriverInfo ));
DBUG((" %d, %d, %d, %p, /* output */\n",
outputDeviceID, numOutputChannels,
outputSampleFormat, outputDriverInfo ));
DBUG((" %g, %d, %d, 0x%x, , %p )\n",
sampleRate, framesPerBuffer, numberOfBuffers,
streamFlags, userData ));
/* Check for parameter errors. */
if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
if( streamPtrPtr == NULL ) return paBadStreamPtr;
if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) )
{
return paInvalidDeviceId;
}
if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
#if SUPPORT_AUDIO_CAPTURE
if( inputDeviceID >= 0 )
{
PaError size = Pa_GetSampleSize( inputSampleFormat );
if( size < 0 ) return size;
bitsPerInputSample = 8 * size;
if( (numInputChannels <= 0) ) return paInvalidChannelCount;
}
#else
if( inputDeviceID >= 0 )
{
return paInvalidChannelCount;
}
#endif /* SUPPORT_AUDIO_CAPTURE */
else
{
if( numInputChannels > 0 ) return paInvalidChannelCount;
bitsPerInputSample = 0;
}
if( outputDeviceID >= 0 )
{
PaError size = Pa_GetSampleSize( outputSampleFormat );
if( size < 0 ) return size;
bitsPerOutputSample = 8 * size;
if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
}
else
{
if( numOutputChannels > 0 ) return paInvalidChannelCount;
bitsPerOutputSample = 0;
}
if( callback == NULL ) return paNullCallback;
/* Allocate and clear stream structure. */
past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
if( past == NULL ) return paInsufficientMemory;
memset( past, 0, sizeof(internalPortAudioStream) );
AddTraceMessage("Pa_OpenStream: past", (long) past );
past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
past->past_FramesPerUserBuffer = framesPerBuffer;
past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() MUST CHECK FOR ZERO! */
past->past_Callback = callback;
past->past_UserData = userData;
past->past_OutputSampleFormat = outputSampleFormat;
past->past_InputSampleFormat = inputSampleFormat;
past->past_OutputDeviceID = outputDeviceID;
past->past_InputDeviceID = inputDeviceID;
past->past_NumInputChannels = numInputChannels;
past->past_NumOutputChannels = numOutputChannels;
past->past_Flags = streamFlags;
/* Check for absurd sample rates. */
if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
{
result = paInvalidSampleRate;
goto cleanup;
}
/* Allocate buffers that may be used for format conversion from user to native buffers. */
if( numInputChannels > 0 )
{
#if PA_VALIDATE_RATE
result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
if( result < 0 )
{
goto cleanup;
}
#else
past->past_SampleRate = sampleRate;
#endif
/* Allocate single Input buffer for passing formatted samples to user callback. */
past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
if( past->past_InputBuffer == NULL )
{
result = paInsufficientMemory;
goto cleanup;
}
}
else
{
past->past_InputBuffer = NULL;
}
/* Allocate single Output buffer. */
if( numOutputChannels > 0 )
{
#if PA_VALIDATE_RATE
result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
if( result < 0 )
{
goto cleanup;
}
#else
past->past_SampleRate = sampleRate;
#endif
past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
if( past->past_OutputBuffer == NULL )
{
result = paInsufficientMemory;
goto cleanup;
}
}
else
{
past->past_OutputBuffer = NULL;
}
result = PaHost_OpenStream( past );
if( result < 0 ) goto cleanup;
*streamPtrPtr = (void *) past;
return result;
cleanup:
if( past != NULL ) Pa_CloseStream( past );
*streamPtrPtr = NULL;
return result;
}
/*************************************************************************/
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData )
{
return Pa_OpenStream(
stream,
((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
numInputChannels, sampleFormat, NULL,
((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
numOutputChannels, sampleFormat, NULL,
sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
}
/*************************************************************************/
PaError Pa_CloseStream( PortAudioStream* stream)
{
PaError result;
internalPortAudioStream *past;
DBUG(("Pa_CloseStream()\n"));
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
Pa_AbortStream( past );
result = PaHost_CloseStream( past );
if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
return result;
}
/*************************************************************************/
PaError Pa_StartStream( PortAudioStream *stream )
{
PaError result = paHostError;
internalPortAudioStream *past;
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
past->past_FrameCount = 0.0;
if( past->past_NumInputChannels > 0 )
{
result = PaHost_StartInput( past );
DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
if( result < 0 ) goto error;
}
if( past->past_NumOutputChannels > 0 )
{
result = PaHost_StartOutput( past );
DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
if( result < 0 ) goto error;
}
result = PaHost_StartEngine( past );
DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
if( result < 0 ) goto error;
return paNoError;
error:
return result;
}
/*************************************************************************/
PaError Pa_StopStream( PortAudioStream *stream )
{
return Pa_KillStream( stream, 0 );
}
/*************************************************************************/
PaError Pa_AbortStream( PortAudioStream *stream )
{
return Pa_KillStream( stream, 1 );
}
/*************************************************************************/
static PaError Pa_KillStream( PortAudioStream *stream, int abort )
{
PaError result = paNoError;
internalPortAudioStream *past;
DBUG(("Pa_StopStream().\n"));
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
{
result = PaHost_StopEngine( past, abort );
DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
if( result < 0 ) goto error;
}
if( past->past_NumInputChannels > 0 )
{
result = PaHost_StopInput( past, abort );
DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
if( result != paNoError ) goto error;
}
if( past->past_NumOutputChannels > 0 )
{
result = PaHost_StopOutput( past, abort );
DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
if( result != paNoError ) goto error;
}
error:
past->past_Usage = 0;
past->past_IfLastExitValid = 0;
return result;
}
/*************************************************************************/
PaError Pa_StreamActive( PortAudioStream *stream )
{
internalPortAudioStream *past;
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
return PaHost_StreamActive( past );
}
/*************************************************************************/
const char *Pa_GetErrorText( PaError errnum )
{
const char *msg;
switch(errnum)
{
case paNoError: msg = "Success"; break;
case paHostError: msg = "Host error."; break;
case paInvalidChannelCount: msg = "Invalid number of channels."; break;
case paInvalidSampleRate: msg = "Invalid sample rate."; break;
case paInvalidDeviceId: msg = "Invalid device ID."; break;
case paInvalidFlag: msg = "Invalid flag."; break;
case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
case paInsufficientMemory: msg = "Insufficient memory."; break;
case paBufferTooBig: msg = "Buffer too big."; break;
case paBufferTooSmall: msg = "Buffer too small."; break;
case paNullCallback: msg = "No callback routine specified."; break;
case paBadStreamPtr: msg = "Invalid stream pointer."; break;
case paTimedOut : msg = "Wait Timed Out."; break;
case paInternalError: msg = "Internal PortAudio Error."; break;
case paDeviceUnavailable: msg = "Device Unavailable."; break;
default: msg = "Illegal error number."; break;
}
return msg;
}
/*
Get CPU Load as a fraction of total CPU time.
A value of 0.5 would imply that PortAudio and the sound generating
callback was consuming roughly 50% of the available CPU time.
The amount may vary depending on CPU load.
This function may be called from the callback function.
*/
double Pa_GetCPULoad( PortAudioStream* stream)
{
internalPortAudioStream *past;
if( stream == NULL ) return (double) paBadStreamPtr;
past = (internalPortAudioStream *) stream;
return past->past_Usage;
}
/*************************************************************************/
internalPortAudioStream* PaHost_GetStreamRepresentation( PortAudioStream *stream )
{
internalPortAudioStream* result = (internalPortAudioStream*) stream;
if( result == NULL || result->past_Magic != PA_MAGIC )
return NULL;
else
return result;
}
/*************************************************************
** Calculate 2 LSB dither signal with a triangular distribution.
** Ranged properly for adding to a 32 bit integer prior to >>15.
** Range of output is +/- 32767
*/
#define PA_DITHER_BITS (15)
#define PA_DITHER_SCALE (1.0f / ((1<<PA_DITHER_BITS)-1))
long PaConvert_TriangularDither( void )
{
static unsigned long previous = 0;
static unsigned long randSeed1 = 22222;
static unsigned long randSeed2 = 5555555;
long current, highPass;
/* Generate two random numbers. */
randSeed1 = (randSeed1 * 196314165) + 907633515;
randSeed2 = (randSeed2 * 196314165) + 907633515;
/* Generate triangular distribution about 0.
* Shift before adding to prevent overflow which would skew the distribution.
* Also shift an extra bit for the high pass filter.
*/
#define DITHER_SHIFT ((32 - PA_DITHER_BITS) + 1)
current = (((long)randSeed1)>>DITHER_SHIFT) + (((long)randSeed2)>>DITHER_SHIFT);
/* High pass filter to reduce audibility. */
highPass = current - previous;
previous = current;
return highPass;
}
/*************************************************************************
** Called by host code.
** Convert input from Int16, call user code, then convert output
** to Int16 format for native use.
** Assumes host native format is paInt16.
** Returns result from user callback.
*/
long Pa_CallConvertInt16( internalPortAudioStream *past,
short *nativeInputBuffer,
short *nativeOutputBuffer )
{
long temp;
int userResult;
unsigned int i;
void *inputBuffer = NULL;
void *outputBuffer = NULL;
#if SUPPORT_AUDIO_CAPTURE
/* Get native data from DirectSound. */
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
{
/* Convert from native format to PA format. */
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
switch(past->past_InputSampleFormat)
{
case paFloat32:
{
float *inBufPtr = (float *) past->past_InputBuffer;
inputBuffer = past->past_InputBuffer;
for( i=0; i<samplesPerBuffer; i++ )
{
inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
}
break;
}
case paInt32:
{
/* Convert 16 bit data to 32 bit integers */
int *inBufPtr = (int *) past->past_InputBuffer;
inputBuffer = past->past_InputBuffer;
for( i=0; i<samplesPerBuffer; i++ )
{
inBufPtr[i] = nativeInputBuffer[i] << 16;
}
break;
}
case paInt16:
{
/* Already in correct format so don't copy. */
inputBuffer = nativeInputBuffer;
break;
}
case paInt8:
{
/* Convert 16 bit data to 8 bit chars */
char *inBufPtr = (char *) past->past_InputBuffer;
inputBuffer = past->past_InputBuffer;
if( past->past_Flags & paDitherOff )
{
for( i=0; i<samplesPerBuffer; i++ )
{
inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
}
}
else
{
for( i=0; i<samplesPerBuffer; i++ )
{
temp = nativeInputBuffer[i];
temp += PaConvert_TriangularDither() >> 8; /* PLB20010820 */
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
inBufPtr[i] = (char)(temp >> 8);
}
}
break;
}
case paUInt8:
{
/* Convert 16 bit data to 8 bit unsigned chars */
unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
inputBuffer = past->past_InputBuffer;
if( past->past_Flags & paDitherOff )
{
for( i=0; i<samplesPerBuffer; i++ )
{
inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
}
}
else
{
/* If you dither then you have to clip because dithering could push the signal out of range! */
for( i=0; i<samplesPerBuffer; i++ )
{
temp = nativeInputBuffer[i];
temp += PaConvert_TriangularDither() >> 8; /* PLB20010820 */
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
inBufPtr[i] = (unsigned char)((temp>>8) + 0x80); /* PLB20010820 */
}
}
break;
}
default:
break;
}
}
#endif /* SUPPORT_AUDIO_CAPTURE */
/* Are we doing output time? */
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
{
/* May already be in native format so just write directly to native buffer. */
outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
nativeOutputBuffer : past->past_OutputBuffer;
}
/*
AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
*/
/* Call user callback routine. */
userResult = past->past_Callback(
inputBuffer,
outputBuffer,
past->past_FramesPerUserBuffer,
past->past_FrameCount,
past->past_UserData );
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
/* Convert to native format if necessary. */
if( outputBuffer != NULL )
{
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
switch(past->past_OutputSampleFormat)
{
case paFloat32:
{
float *outBufPtr = (float *) past->past_OutputBuffer;
if( past->past_Flags & paDitherOff )
{
if( past->past_Flags & paClipOff ) /* NOTHING */
{
for( i=0; i<samplesPerBuffer; i++ )
{
*nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
}
}
else /* CLIP */
{
for( i=0; i<samplesPerBuffer; i++ )
{
temp = (long)(outBufPtr[i] * 32767.0f);
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
}
}
}
else
{
/* If you dither then you have to clip because dithering could push the signal out of range! */
for( i=0; i<samplesPerBuffer; i++ )
{
float dither = PaConvert_TriangularDither()*PA_DITHER_SCALE;
float dithered = (outBufPtr[i] * (32767.0f)) + dither;
temp = (long) (dithered);
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
}
}
break;
}
case paInt32:
{
int *outBufPtr = (int *) past->past_OutputBuffer;
if( past->past_Flags & paDitherOff )
{
for( i=0; i<samplesPerBuffer; i++ )
{
*nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
}
}
else
{
for( i=0; i<samplesPerBuffer; i++ )
{
/* Shift one bit down before dithering so that we have room for overflow from add. */
temp = (outBufPtr[i] >> 1) + PaConvert_TriangularDither();
temp = temp >> 15;
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
}
}
break;
}
case paInt8:
{
char *outBufPtr = (char *) past->past_OutputBuffer;
for( i=0; i<samplesPerBuffer; i++ )
{
*nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
}
break;
}
case paUInt8:
{
unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
for( i=0; i<samplesPerBuffer; i++ )
{
*nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
}
break;
}
default:
break;
}
}
return userResult;
}
/*************************************************************************/
PaError Pa_Initialize( void )
{
if( gInitCount++ > 0 ) return paNoError;
ResetTraceMessages();
return PaHost_Init();
}
PaError Pa_Terminate( void )
{
PaError result = paNoError;
if( gInitCount == 0 ) return paNoError;
else if( --gInitCount == 0 )
{
result = PaHost_Term();
DumpTraceMessages();
}
return result;
}
int PaHost_IsInitialized()
{
return gInitCount;
}
/*************************************************************************/
PaError Pa_GetSampleSize( PaSampleFormat format )
{
int size;
switch(format )
{
case paUInt8:
case paInt8:
size = 1;
break;
case paInt16:
size = 2;
break;
case paPackedInt24:
size = 3;
break;
case paFloat32:
case paInt32:
case paInt24:
size = 4;
break;
default:
size = paSampleFormatNotSupported;
break;
}
return (PaError) size;
}

View file

@ -0,0 +1,83 @@
/*
* $Id$
* Portable Audio I/O Library Trace Facility
* Store trace information in real-time for later printing.
*
* Based on the Open Source API proposed by Ross Bencina
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pa_trace.h"
#if TRACE_REALTIME_EVENTS
static char *traceTextArray[MAX_TRACE_RECORDS];
static int traceIntArray[MAX_TRACE_RECORDS];
static int traceIndex = 0;
static int traceBlock = 0;
/*********************************************************************/
void ResetTraceMessages()
{
traceIndex = 0;
}
/*********************************************************************/
void DumpTraceMessages()
{
int i;
int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS;
printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
for( i=0; i<numDump; i++ )
{
printf("%3d: %s = 0x%08X\n",
i, traceTextArray[i], traceIntArray[i] );
}
ResetTraceMessages();
fflush(stdout);
}
/*********************************************************************/
void AddTraceMessage( char *msg, int data )
{
if( (traceIndex == MAX_TRACE_RECORDS) && (traceBlock == 0) )
{
traceBlock = 1;
/* DumpTraceMessages(); */
}
else if( traceIndex < MAX_TRACE_RECORDS )
{
traceTextArray[traceIndex] = msg;
traceIntArray[traceIndex] = data;
traceIndex++;
}
}
#endif

View file

@ -0,0 +1,67 @@
#ifndef PA_TRACE_H
#define PA_TRACE_H
/*
* $Id$
* Portable Audio I/O Library Trace Facility
* Store trace information in real-time for later printing.
*
* Based on the Open Source API proposed by Ross Bencina
* 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.
*/
#define TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
#define MAX_TRACE_RECORDS (2048)
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/************************************************************************************/
/****************** Prototypes ******************************************************/
/************************************************************************************/
#if TRACE_REALTIME_EVENTS
void DumpTraceMessages();
void ResetTraceMessages();
void AddTraceMessage( char *msg, int data );
#else
#define AddTraceMessage(msg,data) /* noop */
#define ResetTraceMessages() /* noop */
#define DumpTraceMessages() /* noop */
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_TRACE_H */

View file

@ -0,0 +1,463 @@
#ifndef PORT_AUDIO_H
#define PORT_AUDIO_H
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/*
* $Id$
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.audiomulch.com/portaudio/
*
* Copyright (c) 1999-2000 Ross Bencina and 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.
*
*/
typedef int PaError;
typedef enum {
paNoError = 0,
paHostError = -10000,
paInvalidChannelCount,
paInvalidSampleRate,
paInvalidDeviceId,
paInvalidFlag,
paSampleFormatNotSupported,
paBadIODeviceCombination,
paInsufficientMemory,
paBufferTooBig,
paBufferTooSmall,
paNullCallback,
paBadStreamPtr,
paTimedOut,
paInternalError,
paDeviceUnavailable
} PaErrorNum;
/*
Pa_Initialize() is the library initialisation function - call this before
using the library.
*/
PaError Pa_Initialize( void );
/*
Pa_Terminate() is the library termination function - call this after
using the library.
*/
PaError Pa_Terminate( void );
/*
Pa_GetHostError() returns a host specific error code.
This can be called after receiving a PortAudio error code of paHostError.
*/
long Pa_GetHostError( void );
/*
Pa_GetErrorText() translates the supplied PortAudio error number
into a human readable message.
*/
const char *Pa_GetErrorText( PaError errnum );
/*
Sample formats
These are formats used to pass sound data between the callback and the
stream. Each device has a "native" format which may be used when optimum
efficiency or control over conversion is required.
Formats marked "always available" are supported (emulated) by all
PortAudio implementations.
The floating point representation (paFloat32) uses +1.0 and -1.0 as the
maximum and minimum respectively.
paUInt8 is an unsigned 8 bit format where 128 is considered "ground"
*/
typedef unsigned long PaSampleFormat;
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
#define paInt24 ((PaSampleFormat) (1<<3))
#define paPackedInt24 ((PaSampleFormat) (1<<4))
#define paInt8 ((PaSampleFormat) (1<<5))
#define paUInt8 ((PaSampleFormat) (1<<6))
#define paCustomFormat ((PaSampleFormat) (1<<16))
/*
Device enumeration mechanism.
Device ids range from 0 to Pa_CountDevices()-1.
Devices may support input, output or both.
*/
typedef int PaDeviceID;
#define paNoDevice -1
int Pa_CountDevices( void );
typedef struct
{
int structVersion;
const char *name;
int maxInputChannels;
int maxOutputChannels;
/* Number of discrete rates, or -1 if range supported. */
int numSampleRates;
/* Array of supported sample rates, or {min,max} if range supported. */
const double *sampleRates;
PaSampleFormat nativeSampleFormats;
}
PaDeviceInfo;
/*
Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() return the
default device ids for input and output respectively, or paNoDevice if
no device is available.
The result can be passed to Pa_OpenStream().
On the PC, the user can specify a default device by
setting an environment variable. For example, to use device #1.
set PA_RECOMMENDED_OUTPUT_DEVICE=1
The user should first determine the available device ids by using
the supplied application "pa_devs".
*/
PaDeviceID Pa_GetDefaultInputDeviceID( void );
PaDeviceID Pa_GetDefaultOutputDeviceID( void );
/*
Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
for the device specified.
If the device parameter is out of range the function returns NULL.
PortAudio manages the memory referenced by the returned pointer, the client
must not manipulate or free the memory. The pointer is only guaranteed to be
valid between calls to Pa_Initialize() and Pa_Terminate().
*/
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device );
/*
PaTimestamp is used to represent a continuous sample clock with arbitrary
start time that can be used for syncronization. The type is used for the
outTime argument to the PortAudioCallback and as the result of Pa_StreamTime()
*/
typedef double PaTimestamp;
/*
PortAudioCallback is implemented by PortAudio clients.
inputBuffer and outputBuffer are arrays of interleaved samples,
the format, packing and number of channels used by the buffers are
determined by parameters to Pa_OpenStream() (see below).
framesPerBuffer is the number of sample frames to be processed by the callback.
outTime is the time in samples when the buffer(s) processed by
this callback will begin being played at the audio output.
See also Pa_StreamTime()
userData is the value of a user supplied pointer passed to Pa_OpenStream()
intended for storing synthesis data etc.
return value:
The callback can return a non-zero value to stop the stream. This may be
useful in applications such as soundfile players where a specific duration
of output is required. However, it is not necessary to utilise this mechanism
as StopStream() will also terminate the stream. A callback returning a
non-zero value must fill the entire outputBuffer.
NOTE: None of the other stream functions may be called from within the
callback function except for Pa_GetCPULoad().
*/
typedef int (PortAudioCallback)(
void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/*
Stream flags
These flags may be supplied (ored together) in the streamFlags argument to
the Pa_OpenStream() function.
*/
#define paNoFlag (0)
#define paClipOff (1<<0) /* disable default clipping of out of range samples */
#define paDitherOff (1<<1) /* disable default dithering */
#define paPlatformSpecificFlags (0x00010000)
typedef unsigned long PaStreamFlags;
/*
A single PortAudioStream provides multiple channels of real-time
input and output audio streaming to a client application.
Pointers to PortAudioStream objects are passed between PortAudio functions.
*/
typedef void PortAudioStream;
#define PaStream PortAudioStream
/*
Pa_OpenStream() opens a stream for either input, output or both.
stream is the address of a PortAudioStream pointer which will receive
a pointer to the newly opened stream.
inputDevice is the id of the device used for input (see PaDeviceID above.)
inputDevice may be paNoDevice to indicate that an input device is not required.
numInputChannels is the number of channels of sound to be delivered to the
callback. It can range from 1 to the value of maxInputChannels in the
PaDeviceInfo record for the device specified by the inputDevice parameter.
If inputDevice is paNoDevice numInputChannels is ignored.
inputSampleFormat is the sample format of inputBuffer provided to the callback
function. inputSampleFormat may be any of the formats described by the
PaSampleFormat enumeration (see above). PortAudio guarantees support for
the device's native formats (nativeSampleFormats in the device info record)
and additionally 16 and 32 bit integer and 32 bit floating point formats.
Support for other formats is implementation defined.
inputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
inputDriverInfo is never required for correct operation. If not used
inputDriverInfo should be NULL.
outputDevice is the id of the device used for output (see PaDeviceID above.)
outputDevice may be paNoDevice to indicate that an output device is not required.
numOutputChannels is the number of channels of sound to be supplied by the
callback. See the definition of numInputChannels above for more details.
outputSampleFormat is the sample format of the outputBuffer filled by the
callback function. See the definition of inputSampleFormat above for more
details.
outputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
outputDriverInfo is never required for correct operation. If not used
outputDriverInfo should be NULL.
sampleRate is the desired sampleRate. For full-duplex streams it is the
sample rate for both input and output
framesPerBuffer is the length in sample frames of all internal sample buffers
used for communication with platform specific audio routines. Wherever
possible this corresponds to the framesPerBuffer parameter passed to the
callback function.
numberOfBuffers is the number of buffers used for multibuffered communication
with the platform specific audio routines. If you pass zero, then an optimum
value will be chosen for you internally. This parameter is provided only
as a guide - and does not imply that an implementation must use multibuffered
i/o when reliable double buffering is available (such as SndPlayDoubleBuffer()
on the Macintosh.)
streamFlags may contain a combination of flags ORed together.
These flags modify the behaviour of the streaming process. Some flags may only
be relevant to certain buffer formats.
callback is a pointer to a client supplied function that is responsible
for processing and filling input and output buffers (see above for details.)
userData is a client supplied pointer which is passed to the callback
function. It could for example, contain a pointer to instance data necessary
for processing the audio buffers.
return value:
Upon success Pa_OpenStream() returns PaNoError and places a pointer to a
valid PortAudioStream in the stream argument. The stream is inactive (stopped).
If a call to Pa_OpenStream() fails a non-zero error code is returned (see
PaError above) and the value of stream is invalid.
*/
PaError Pa_OpenStream( PortAudioStream** stream,
PaDeviceID inputDevice,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDevice,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PaStreamFlags streamFlags,
PortAudioCallback *callback,
void *userData );
/*
Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that opens
the default input and/or output devices. Most parameters have identical meaning
to their Pa_OpenStream() counterparts, with the following exceptions:
If either numInputChannels or numOutputChannels is 0 the respective device
is not opened. This has the same effect as passing paNoDevice in the device
arguments to Pa_OpenStream().
sampleFormat applies to both the input and output buffers.
*/
PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData );
/*
Pa_CloseStream() closes an audio stream, flushing any pending buffers.
*/
PaError Pa_CloseStream( PortAudioStream* );
/*
Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
Pa_StopStream() waits until all pending audio buffers have been played.
Pa_AbortStream() stops playing immediately without waiting for pending
buffers to complete.
*/
PaError Pa_StartStream( PortAudioStream *stream );
PaError Pa_StopStream( PortAudioStream *stream );
PaError Pa_AbortStream( PortAudioStream *stream );
/*
Pa_StreamActive() returns one (1) when the stream is active (ie playing
or recording audio), zero (0) when not playing, or a negative error number
if the stream is invalid.
The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
but may also become inactive if the callback returns a non-zero value.
In the latter case, the stream is considered inactive after the last
buffer has finished playing.
*/
PaError Pa_StreamActive( PortAudioStream *stream );
/*
Pa_StreamTime() returns the current output time in samples for the stream.
This time may be used as a time reference (for example synchronizing audio to
MIDI).
*/
PaTimestamp Pa_StreamTime( PortAudioStream *stream );
/*
Pa_GetCPULoad() returns the CPU Load for the stream.
The "CPU Load" is a fraction of total CPU time consumed by the stream's
audio processing routines including, but not limited to the client supplied
callback.
A value of 0.5 would imply that PortAudio and the sound generating
callback was consuming roughly 50% of the available CPU time.
This function may be called from the callback function or the application.
*/
double Pa_GetCPULoad( PortAudioStream* stream );
/*
Pa_GetMinNumBuffers() returns the minimum number of buffers required by
the current host based on minimum latency.
On the PC, for the DirectSound implementation, latency can be optionally set
by user by setting an environment variable.
For example, to set latency to 200 msec, put:
set PA_MIN_LATENCY_MSEC=200
in the AUTOEXEC.BAT file and reboot.
If the environment variable is not set, then the latency will be determined
based on the OS. Windows NT has higher latency than Win95.
*/
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
/*
Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds.
You may sleep longer than the requested time so don't rely on this for
accurate musical timing.
Pa_Sleep() is provided as a convenience for authors of portable code (such as
the tests and examples in the PortAudio distribution.)
*/
void Pa_Sleep( long msec );
/*
Pa_GetSampleSize() returns the size in bytes of a single sample in the
supplied PaSampleFormat, or paSampleFormatNotSupported if the format is
no supported.
*/
PaError Pa_GetSampleSize( PaSampleFormat format );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PORT_AUDIO_H */

View file

@ -0,0 +1,184 @@
/*
* PortAudio Portable Real-Time Audio Library
* PortAudio DLL Header File
* Latest version available at: http://www.audiomulch.com/portaudio/
*
* Copyright (c) 1999-2000 Ross Bencina and 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.
*
*/
// changed by zplane.developement in order to generate a DLL
#ifndef __PADLLENTRY_HEADER_INCLUDED__
#define __PADLLENTRY_HEADER_INCLUDED__
typedef int PaError;
typedef enum {
paNoError = 0,
paHostError = -10000,
paInvalidChannelCount,
paInvalidSampleRate,
paInvalidDeviceId,
paInvalidFlag,
paSampleFormatNotSupported,
paBadIODeviceCombination,
paInsufficientMemory,
paBufferTooBig,
paBufferTooSmall,
paNullCallback,
paBadStreamPtr,
paTimedOut,
paInternalError
} PaErrorNum;
typedef unsigned long PaSampleFormat;
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
#define paInt24 ((PaSampleFormat) (1<<3))
#define paPackedInt24 ((PaSampleFormat) (1<<4))
#define paInt8 ((PaSampleFormat) (1<<5))
#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
#define paCustomFormat ((PaSampleFormat) (1<<16))
typedef int PaDeviceID;
#define paNoDevice -1
typedef struct
{
int structVersion;
const char *name;
int maxInputChannels;
int maxOutputChannels;
/* Number of discrete rates, or -1 if range supported. */
int numSampleRates;
/* Array of supported sample rates, or {min,max} if range supported. */
const double *sampleRates;
PaSampleFormat nativeSampleFormats;
}
PaDeviceInfo;
typedef double PaTimestamp;
typedef int (PortAudioCallback)(
void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
#define paNoFlag (0)
#define paClipOff (1<<0) /* disable default clipping of out of range samples */
#define paDitherOff (1<<1) /* disable default dithering */
#define paPlatformSpecificFlags (0x00010000)
typedef unsigned long PaStreamFlags;
typedef void PortAudioStream;
#define PaStream PortAudioStream
extern PaError (__cdecl* Pa_Initialize)( void );
extern PaError (__cdecl* Pa_Terminate)( void );
extern long (__cdecl* Pa_GetHostError)( void );
extern const char* (__cdecl* Pa_GetErrorText)( PaError );
extern int (__cdecl* Pa_CountDevices)(void);
extern PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void );
extern PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void );
extern const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID);
extern PaError (__cdecl* Pa_OpenStream)(
PortAudioStream ** ,
PaDeviceID ,
int ,
PaSampleFormat ,
void *,
PaDeviceID ,
int ,
PaSampleFormat ,
void *,
double ,
unsigned long ,
unsigned long ,
unsigned long ,
PortAudioCallback *,
void * );
extern PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData );
extern PaError (__cdecl* Pa_CloseStream)( PortAudioStream* );
extern PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream );
extern PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream );
extern PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream );
extern PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream );
extern PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream );
extern double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream );
extern int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate );
extern void (__cdecl* Pa_Sleep)( long msec );
extern PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format );
#endif // __PADLLENTRY_HEADER_INCLUDED__

View file

@ -0,0 +1,203 @@
//////////////////////////////////////////////////////////////////////////
HINSTANCE pPaDll;
/*
the function pointers to the PortAudio DLLs
*/
PaError (__cdecl* Pa_Initialize)( void );
PaError (__cdecl* Pa_Terminate)( void );
long (__cdecl* Pa_GetHostError)( void );
const char* (__cdecl* Pa_GetErrorText)( PaError );
int (__cdecl* Pa_CountDevices)(void);
PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void );
PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void );
const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID);
PaError (__cdecl* Pa_OpenStream)(
PortAudioStream ** ,
PaDeviceID ,
int ,
PaSampleFormat ,
void *,
PaDeviceID ,
int ,
PaSampleFormat ,
void *,
double ,
unsigned long ,
unsigned long ,
unsigned long ,
PortAudioCallback *,
void * );
PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData );
PaError (__cdecl* Pa_CloseStream)( PortAudioStream* );
PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream );
PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream );
PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream );
PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream );
PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream );
double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream );
int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate );
void (__cdecl* Pa_Sleep)( long msec );
PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format );
//////////////////////////////////////////////////////////////////////////
...
ZERROR AudioEngine::DirectXSupport(ZBOOL bSupDX)
{
if (bSupDX)
if (CheckForDirectXSupport())
bSupportDirectX = _TRUE;
else
return _NO_SOUND;
else
bSupportDirectX = _FALSE;
return _NO_ERROR;
}
ZBOOL AudioEngine::CheckForDirectXSupport()
{
HMODULE pTestDXLib;
FARPROC pFunctionality;
pTestDXLib=LoadLibrary("DSOUND");
if (pTestDXLib!=NULL) // check if there is a DirectSound
{
pFunctionality = GetProcAddress(pTestDXLib, (char*) 7);
if (pFunctionality!=NULL)
{
FreeLibrary(pTestDXLib);
return _TRUE;
}
else
{
FreeLibrary(pTestDXLib);
return _FALSE;
}
}
else
return _FALSE;
}
ZERROR AudioEngine::LoadPALib()
{
#ifdef _DEBUG
if (bSupportDirectX)
pPaDll = LoadLibrary("PA_DXD");
else
pPaDll = LoadLibrary("PA_MMED");
#else
if (bSupportDirectX)
pPaDll = LoadLibrary("PA_DX");
else
pPaDll = LoadLibrary("PA_MME");
#endif
if (pPaDll!=NULL)
{
Pa_Initialize = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Initialize");
Pa_Terminate = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Terminate");
Pa_GetHostError = (long (__cdecl* )( void )) GetProcAddress(pPaDll,"Pa_GetHostError");
Pa_GetErrorText = (const char* (__cdecl* )( PaError )) GetProcAddress(pPaDll,"Pa_GetErrorText");
Pa_CountDevices = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_CountDevices");
Pa_GetDefaultInputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultInputDeviceID");
Pa_GetDefaultOutputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultOutputDeviceID");
Pa_GetDeviceInfo = (const PaDeviceInfo* (__cdecl* )( PaDeviceID)) GetProcAddress(pPaDll,"Pa_GetDeviceInfo");
Pa_OpenStream = ( PaError (__cdecl* )(
PortAudioStream ** ,
PaDeviceID ,
int ,
PaSampleFormat ,
void *,
PaDeviceID ,
int ,
PaSampleFormat ,
void *,
double ,
unsigned long ,
unsigned long ,
unsigned long ,
PortAudioCallback *,
void * )) GetProcAddress(pPaDll,"Pa_OpenStream");
Pa_OpenDefaultStream = (PaError (__cdecl* )( PortAudioStream** ,
int ,
int ,
PaSampleFormat ,
double ,
unsigned long ,
unsigned long ,
PortAudioCallback *,
void * )) GetProcAddress(pPaDll,"Pa_OpenDefaultStream");
Pa_CloseStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_CloseStream");
Pa_StartStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StartStream");
Pa_StopStream = (PaError (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_StopStream");
Pa_AbortStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_AbortStream");
Pa_StreamActive = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StreamActive");
Pa_StreamTime = (PaTimestamp (__cdecl* )( PortAudioStream *))GetProcAddress(pPaDll,"Pa_StreamTime");
Pa_GetCPULoad = (double (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_GetCPULoad");
Pa_GetMinNumBuffers = (int (__cdecl* )( int , double )) GetProcAddress(pPaDll,"Pa_GetMinNumBuffers");
Pa_Sleep = (void (__cdecl* )( long )) GetProcAddress(pPaDll,"Pa_Sleep");
Pa_GetSampleSize = (PaError (__cdecl* )( PaSampleFormat )) GetProcAddress(pPaDll,"Pa_GetSampleSize");
return _NO_ERROR;
}
else
return _DLL_NOT_FOUND;
}
ZERROR AudioEngine::UnLoadPALib()
{
if (pPaDll!=NULL)
FreeLibrary(pPaDll);
return _NO_ERROR;
}
...

View file

@ -0,0 +1,827 @@
/*
* Portable Audio I/O Library
* Host Independant Layer
*
* Based on the Open Source API proposed by Ross Bencina
* 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:
PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
#ifdef _WIN32
#ifndef __MWERKS__
#include <memory.h>
#endif /* __MWERKS__ */
#else /* !_WIN32 */
#include <memory.h>
#endif /* _WIN32 */
#include "portaudio.h"
#include "pa_host.h"
#include "pa_trace.h"
/* The reason we might NOT want to validate the rate before opening the stream
* is because many DirectSound drivers lie about the rates they actually support.
*/
#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */
/*
O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion
*/
#ifndef FALSE
#define FALSE (0)
#define TRUE (!FALSE)
#endif
#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x) /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) */
static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */
static PaError Pa_KillStream( PortAudioStream *stream, int abort );
/***********************************************************************/
int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate )
{
double err, minErr = allowableError;
int i, bestFit = -1;
for( i=0; i<numRates; i++ )
{
err = fabs( frameRate - rateTable[i] );
if( err < minErr )
{
minErr = err;
bestFit = i;
}
}
return bestFit;
}
/**************************************************************************
** Make sure sample rate is legal and also convert to enumeration for driver.
*/
PaError PaHost_ValidateSampleRate( PaDeviceID id, double requestedFrameRate,
double *closestFrameRatePtr )
{
long bestRateIndex;
const PaDeviceInfo *pdi;
pdi = Pa_GetDeviceInfo( id );
if( pdi == NULL ) return paInvalidDeviceId;
if( pdi->numSampleRates == -1 )
{
/* Is it out of range? */
if( (requestedFrameRate < pdi->sampleRates[0]) ||
(requestedFrameRate > pdi->sampleRates[1]) )
{
return paInvalidSampleRate;
}
*closestFrameRatePtr = requestedFrameRate;
}
else
{
bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate );
if( bestRateIndex < 0 ) return paInvalidSampleRate;
*closestFrameRatePtr = pdi->sampleRates[bestRateIndex];
}
return paNoError;
}
/*************************************************************************/
DLL_API PaError Pa_OpenStream(
PortAudioStream** streamPtrPtr,
PaDeviceID inputDeviceID,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDeviceID,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
unsigned long streamFlags,
PortAudioCallback *callback,
void *userData )
{
internalPortAudioStream *past = NULL;
PaError result = paNoError;
int bitsPerInputSample;
int bitsPerOutputSample;
/* Print passed parameters. */
DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n",
streamPtrPtr, inputDeviceID, numInputChannels,
inputSampleFormat, inputDriverInfo ));
DBUG((" %d, %d, %d, %p, /* output */\n",
outputDeviceID, numOutputChannels,
outputSampleFormat, outputDriverInfo ));
DBUG((" %g, %d, %d, 0x%x, , %p )\n",
sampleRate, framesPerBuffer, numberOfBuffers,
streamFlags, userData ));
/* Check for parameter errors. */
if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag;
if( streamPtrPtr == NULL ) return paBadStreamPtr;
if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */
if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */
if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId;
if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) return paInvalidDeviceId;
if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount;
#if SUPPORT_AUDIO_CAPTURE
if( inputDeviceID >= 0 )
{
PaError size = Pa_GetSampleSize( inputSampleFormat );
if( size < 0 ) return size;
bitsPerInputSample = 8 * size;
if( (numInputChannels <= 0) ) return paInvalidChannelCount;
}
#else
if( inputDeviceID >= 0 )
{
return paInvalidChannelCount;
}
#endif /* SUPPORT_AUDIO_CAPTURE */
else
{
if( numInputChannels > 0 ) return paInvalidChannelCount;
bitsPerInputSample = 0;
}
if( outputDeviceID >= 0 )
{
PaError size = Pa_GetSampleSize( outputSampleFormat );
if( size < 0 ) return size;
bitsPerOutputSample = 8 * size;
if( (numOutputChannels <= 0) ) return paInvalidChannelCount;
}
else
{
if( numOutputChannels > 0 ) return paInvalidChannelCount;
bitsPerOutputSample = 0;
}
if( callback == NULL ) return paNullCallback;
/* Allocate and clear stream structure. */
past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) );
if( past == NULL ) return paInsufficientMemory;
memset( past, 0, sizeof(internalPortAudioStream) );
AddTraceMessage("Pa_OpenStream: past", (long) past );
past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */
past->past_FramesPerUserBuffer = framesPerBuffer;
past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */
past->past_Callback = callback;
past->past_UserData = userData;
past->past_OutputSampleFormat = outputSampleFormat;
past->past_InputSampleFormat = inputSampleFormat;
past->past_OutputDeviceID = outputDeviceID;
past->past_InputDeviceID = inputDeviceID;
past->past_NumInputChannels = numInputChannels;
past->past_NumOutputChannels = numOutputChannels;
past->past_Flags = streamFlags;
/* Check for absurd sample rates. */
if( (sampleRate < 1000.0) || (sampleRate > 200000.0) )
{
result = paInvalidSampleRate;
goto cleanup;
}
/* Allocate buffers that may be used for format conversion from user to native buffers. */
if( numInputChannels > 0 )
{
#if PA_VALIDATE_RATE
result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate );
if( result < 0 )
{
goto cleanup;
}
#else
past->past_SampleRate = sampleRate;
#endif
/* Allocate single Input buffer. */
past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8);
past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize);
if( past->past_InputBuffer == NULL )
{
result = paInsufficientMemory;
goto cleanup;
}
}
else
{
past->past_InputBuffer = NULL;
}
/* Allocate single Output buffer. */
if( numOutputChannels > 0 )
{
#if PA_VALIDATE_RATE
result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate );
if( result < 0 )
{
goto cleanup;
}
#else
past->past_SampleRate = sampleRate;
#endif
past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8);
past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize);
if( past->past_OutputBuffer == NULL )
{
result = paInsufficientMemory;
goto cleanup;
}
}
else
{
past->past_OutputBuffer = NULL;
}
result = PaHost_OpenStream( past );
if( result < 0 ) goto cleanup;
*streamPtrPtr = (void *) past;
return result;
cleanup:
if( past != NULL ) Pa_CloseStream( past );
*streamPtrPtr = NULL;
return result;
}
/*************************************************************************/
DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData )
{
return Pa_OpenStream(
stream,
((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice),
numInputChannels, sampleFormat, NULL,
((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice),
numOutputChannels, sampleFormat, NULL,
sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData );
}
/*************************************************************************/
DLL_API PaError Pa_CloseStream( PortAudioStream* stream)
{
PaError result;
internalPortAudioStream *past;
DBUG(("Pa_CloseStream()\n"));
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
Pa_AbortStream( past );
result = PaHost_CloseStream( past );
if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize );
if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize );
PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) );
return result;
}
/*************************************************************************/
DLL_API PaError Pa_StartStream( PortAudioStream *stream )
{
PaError result = paHostError;
internalPortAudioStream *past;
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
past->past_FrameCount = 0.0;
if( past->past_NumInputChannels > 0 )
{
result = PaHost_StartInput( past );
DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result));
if( result < 0 ) goto error;
}
if( past->past_NumOutputChannels > 0 )
{
result = PaHost_StartOutput( past );
DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result));
if( result < 0 ) goto error;
}
result = PaHost_StartEngine( past );
DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result));
if( result < 0 ) goto error;
return paNoError;
error:
return result;
}
/*************************************************************************/
DLL_API PaError Pa_StopStream( PortAudioStream *stream )
{
return Pa_KillStream( stream, 0 );
}
/*************************************************************************/
DLL_API PaError Pa_AbortStream( PortAudioStream *stream )
{
return Pa_KillStream( stream, 1 );
}
/*************************************************************************/
static PaError Pa_KillStream( PortAudioStream *stream, int abort )
{
PaError result = paNoError;
internalPortAudioStream *past;
DBUG(("Pa_StopStream().\n"));
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) )
{
result = PaHost_StopEngine( past, abort );
DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result));
if( result < 0 ) goto error;
}
if( past->past_NumInputChannels > 0 )
{
result = PaHost_StopInput( past, abort );
DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result));
if( result != paNoError ) goto error;
}
if( past->past_NumOutputChannels > 0 )
{
result = PaHost_StopOutput( past, abort );
DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result));
if( result != paNoError ) goto error;
}
error:
past->past_Usage = 0;
past->past_IfLastExitValid = 0;
return result;
}
/*************************************************************************/
DLL_API PaError Pa_StreamActive( PortAudioStream *stream )
{
internalPortAudioStream *past;
if( stream == NULL ) return paBadStreamPtr;
past = (internalPortAudioStream *) stream;
return PaHost_StreamActive( past );
}
/*************************************************************************/
DLL_API const char *Pa_GetErrorText( PaError errnum )
{
const char *msg;
switch(errnum)
{
case paNoError: msg = "Success"; break;
case paHostError: msg = "Host error."; break;
case paInvalidChannelCount: msg = "Invalid number of channels."; break;
case paInvalidSampleRate: msg = "Invalid sample rate."; break;
case paInvalidDeviceId: msg = "Invalid device ID."; break;
case paInvalidFlag: msg = "Invalid flag."; break;
case paSampleFormatNotSupported: msg = "Sample format not supported"; break;
case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break;
case paInsufficientMemory: msg = "Insufficient memory."; break;
case paBufferTooBig: msg = "Buffer too big."; break;
case paBufferTooSmall: msg = "Buffer too small."; break;
case paNullCallback: msg = "No callback routine specified."; break;
case paBadStreamPtr: msg = "Invalid stream pointer."; break;
case paTimedOut : msg = "Wait Timed Out."; break;
case paInternalError: msg = "Internal PortAudio Error."; break;
default: msg = "Illegal error number."; break;
}
return msg;
}
/*
Get CPU Load as a fraction of total CPU time.
A value of 0.5 would imply that PortAudio and the sound generating
callback was consuming roughly 50% of the available CPU time.
The amount may vary depending on CPU load.
This function may be called from the callback function.
*/
DLL_API double Pa_GetCPULoad( PortAudioStream* stream)
{
internalPortAudioStream *past;
if( stream == NULL ) return (double) paBadStreamPtr;
past = (internalPortAudioStream *) stream;
return past->past_Usage;
}
/*************************************************************
** Calculate 2 LSB dither signal with a triangular distribution.
** Ranged properly for adding to a 32 bit integer prior to >>15.
*/
#define DITHER_BITS (15)
#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1))
static long Pa_TriangularDither( void )
{
static unsigned long previous = 0;
static unsigned long randSeed1 = 22222;
static unsigned long randSeed2 = 5555555;
long current, highPass;
/* Generate two random numbers. */
randSeed1 = (randSeed1 * 196314165) + 907633515;
randSeed2 = (randSeed2 * 196314165) + 907633515;
/* Generate triangular distribution about 0. */
current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS));
/* High pass filter to reduce audibility. */
highPass = current - previous;
previous = current;
return highPass;
}
/*************************************************************************
** Called by host code.
** Convert input from Int16, call user code, then convert output
** to Int16 format for native use.
** Assumes host native format is paInt16.
** Returns result from user callback.
*/
long Pa_CallConvertInt16( internalPortAudioStream *past,
short *nativeInputBuffer,
short *nativeOutputBuffer )
{
long temp;
long bytesEmpty = 0;
long bytesFilled = 0;
int userResult;
unsigned int i;
void *inputBuffer = NULL;
void *outputBuffer = NULL;
#if SUPPORT_AUDIO_CAPTURE
/* Get native data from DirectSound. */
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
{
/* Convert from native format to PA format. */
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels;
switch(past->past_InputSampleFormat)
{
case paFloat32:
{
float *inBufPtr = (float *) past->past_InputBuffer;
inputBuffer = past->past_InputBuffer;
for( i=0; i<samplesPerBuffer; i++ )
{
inBufPtr[i] = nativeInputBuffer[i] * (1.0f / 32767.0f);
}
break;
}
case paInt32:
{
/* Convert 16 bit data to 32 bit integers */
int *inBufPtr = (int *) past->past_InputBuffer;
inputBuffer = past->past_InputBuffer;
for( i=0; i<samplesPerBuffer; i++ )
{
inBufPtr[i] = nativeInputBuffer[i] << 16;
}
break;
}
case paInt16:
{
/* Already in correct format so don't copy. */
inputBuffer = nativeInputBuffer;
break;
}
case paInt8:
{
/* Convert 16 bit data to 8 bit chars */
char *inBufPtr = (char *) past->past_InputBuffer;
inputBuffer = past->past_InputBuffer;
if( past->past_Flags & paDitherOff )
{
for( i=0; i<samplesPerBuffer; i++ )
{
inBufPtr[i] = (char)(nativeInputBuffer[i] >> 8);
}
}
else
{
for( i=0; i<samplesPerBuffer; i++ )
{
temp = nativeInputBuffer[i];
temp += Pa_TriangularDither() >> 7;
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
inBufPtr[i] = (char)(temp >> 8);
}
}
break;
}
case paUInt8:
{
/* Convert 16 bit data to 8 bit unsigned chars */
unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer;
inputBuffer = past->past_InputBuffer;
if( past->past_Flags & paDitherOff )
{
for( i=0; i<samplesPerBuffer; i++ )
{
inBufPtr[i] = ((unsigned char)(nativeInputBuffer[i] >> 8)) + 0x80;
}
}
else
{
/* If you dither then you have to clip because dithering could push the signal out of range! */
for( i=0; i<samplesPerBuffer; i++ )
{
temp = nativeInputBuffer[i];
temp += Pa_TriangularDither() >> 7;
temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
inBufPtr[i] = (unsigned char)(temp + 0x80);
}
}
break;
}
default:
break;
}
}
#endif /* SUPPORT_AUDIO_CAPTURE */
/* Are we doing output time? */
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
{
/* May already be in native format so just write directly to native buffer. */
outputBuffer = (past->past_OutputSampleFormat == paInt16) ?
nativeOutputBuffer : past->past_OutputBuffer;
}
/*
AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
*/
/* Call user callback routine. */
userResult = past->past_Callback(
inputBuffer,
outputBuffer,
past->past_FramesPerUserBuffer,
past->past_FrameCount,
past->past_UserData );
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
/* Convert to native format if necessary. */
if( outputBuffer != NULL )
{
unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels;
switch(past->past_OutputSampleFormat)
{
case paFloat32:
{
float *outBufPtr = (float *) past->past_OutputBuffer;
if( past->past_Flags & paDitherOff )
{
if( past->past_Flags & paClipOff ) /* NOTHING */
{
for( i=0; i<samplesPerBuffer; i++ )
{
*nativeOutputBuffer++ = (short) (outBufPtr[i] * (32767.0f));
}
}
else /* CLIP */
{
for( i=0; i<samplesPerBuffer; i++ )
{
temp = (long)(outBufPtr[i] * 32767.0f);
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
}
}
}
else
{
/* If you dither then you have to clip because dithering could push the signal out of range! */
for( i=0; i<samplesPerBuffer; i++ )
{
float dither = Pa_TriangularDither()*DITHER_SCALE;
float dithered = (outBufPtr[i] * (32767.0f)) + dither;
temp = (long) (dithered);
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
}
}
break;
}
case paInt32:
{
int *outBufPtr = (int *) past->past_OutputBuffer;
if( past->past_Flags & paDitherOff )
{
for( i=0; i<samplesPerBuffer; i++ )
{
*nativeOutputBuffer++ = (short) (outBufPtr[i] >> 16 );
}
}
else
{
for( i=0; i<samplesPerBuffer; i++ )
{
/* Shift one bit down before dithering so that we have room for overflow from add. */
temp = (outBufPtr[i] >> 1) + Pa_TriangularDither();
temp = temp >> 15;
*nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp));
}
}
break;
}
case paInt8:
{
char *outBufPtr = (char *) past->past_OutputBuffer;
for( i=0; i<samplesPerBuffer; i++ )
{
*nativeOutputBuffer++ = ((short)outBufPtr[i]) << 8;
}
break;
}
case paUInt8:
{
unsigned char *outBufPtr = (unsigned char *) past->past_OutputBuffer;
for( i=0; i<samplesPerBuffer; i++ )
{
*nativeOutputBuffer++ = ((short)(outBufPtr[i] - 0x80)) << 8;
}
break;
}
default:
break;
}
}
return userResult;
}
/*************************************************************************
** Called by host code.
** Convert input from Float32, call user code, then convert output
** to Float32 format for native use.
** Assumes host native format is Float32.
** Returns result from user callback.
** FIXME - Unimplemented for formats other than paFloat32!!!!
*/
long Pa_CallConvertFloat32( internalPortAudioStream *past,
float *nativeInputBuffer,
float *nativeOutputBuffer )
{
long bytesEmpty = 0;
long bytesFilled = 0;
int userResult;
void *inputBuffer = NULL;
void *outputBuffer = NULL;
/* Get native data from DirectSound. */
if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) )
{
inputBuffer = nativeInputBuffer; // FIXME
}
/* Are we doing output time? */
if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) )
{
/* May already be in native format so just write directly to native buffer. */
outputBuffer = (past->past_OutputSampleFormat == paFloat32) ?
nativeOutputBuffer : past->past_OutputBuffer;
}
/*
AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer );
AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer );
*/
/* Call user callback routine. */
userResult = past->past_Callback(
inputBuffer,
outputBuffer,
past->past_FramesPerUserBuffer,
past->past_FrameCount,
past->past_UserData );
past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer;
/* Convert to native format if necessary. */ // FIXME
return userResult;
}
/*************************************************************************/
DLL_API PaError Pa_Initialize( void )
{
if( gInitCount++ > 0 ) return paNoError;
ResetTraceMessages();
return PaHost_Init();
}
DLL_API PaError Pa_Terminate( void )
{
PaError result = paNoError;
if( gInitCount == 0 ) return paNoError;
else if( --gInitCount == 0 )
{
result = PaHost_Term();
DumpTraceMessages();
}
return result;
}
/*************************************************************************/
DLL_API PaError Pa_GetSampleSize( PaSampleFormat format )
{
int size;
switch(format )
{
case paUInt8:
case paInt8:
size = 1;
break;
case paInt16:
size = 2;
break;
case paPackedInt24:
size = 3;
break;
case paFloat32:
case paInt32:
case paInt24:
size = 4;
break;
default:
size = paSampleFormatNotSupported;
break;
}
return (PaError) size;
}

View file

@ -0,0 +1,439 @@
#ifndef PORT_AUDIO_H
#define PORT_AUDIO_H
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/*
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.audiomulch.com/portaudio/
*
* Copyright (c) 1999-2000 Ross Bencina and 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.
*
*/
// added by zplane.developement in order to generate a DLL
#if defined(PA_MME_EXPORTS) || defined(PA_DX_EXPORTS)
#define DLL_API __declspec( dllexport )
#elif defined(_LIB) || defined(_STATIC_LINK) || defined(_STATIC_APP)
#define DLL_API
#else
#define DLL_API __declspec(dllexport)
#endif
typedef int PaError;
typedef enum {
paNoError = 0,
paHostError = -10000,
paInvalidChannelCount,
paInvalidSampleRate,
paInvalidDeviceId,
paInvalidFlag,
paSampleFormatNotSupported,
paBadIODeviceCombination,
paInsufficientMemory,
paBufferTooBig,
paBufferTooSmall,
paNullCallback,
paBadStreamPtr,
paTimedOut,
paInternalError
} PaErrorNum;
/*
Pa_Initialize() is the library initialisation function - call this before
using the library.
*/
DLL_API PaError Pa_Initialize( void );
/*
Pa_Terminate() is the library termination function - call this after
using the library.
*/
DLL_API PaError Pa_Terminate( void );
/*
Return host specific error.
This can be called after receiving a paHostError.
*/
DLL_API long Pa_GetHostError( void );
/*
Translate the error number into a human readable message.
*/
DLL_API const char *Pa_GetErrorText( PaError errnum );
/*
Sample formats
These are formats used to pass sound data between the callback and the
stream. Each device has a "native" format which may be used when optimum
efficiency or control over conversion is required.
Formats marked "always available" are supported (emulated) by all devices.
The floating point representation uses +1.0 and -1.0 as the respective
maximum and minimum.
*/
typedef unsigned long PaSampleFormat;
#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/
#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/
#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/
#define paInt24 ((PaSampleFormat) (1<<3))
#define paPackedInt24 ((PaSampleFormat) (1<<4))
#define paInt8 ((PaSampleFormat) (1<<5))
#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */
#define paCustomFormat ((PaSampleFormat) (1<<16))
/*
Device enumeration mechanism.
Device ids range from 0 to Pa_CountDevices()-1.
Devices may support input, output or both. Device 0 is always the "default"
device and should support at least stereo in and out if that is available
on the taget platform _even_ if this involves kludging an input/output
device on platforms that usually separate input from output. Other platform
specific devices are specified by positive device ids.
*/
typedef int PaDeviceID;
#define paNoDevice -1
typedef struct
{
int structVersion;
const char *name;
int maxInputChannels;
int maxOutputChannels;
/* Number of discrete rates, or -1 if range supported. */
int numSampleRates;
/* Array of supported sample rates, or {min,max} if range supported. */
const double *sampleRates;
PaSampleFormat nativeSampleFormats;
}
PaDeviceInfo;
DLL_API int Pa_CountDevices();
/*
Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID()
Return the default device ID or paNoDevice if there is no devices.
The result can be passed to Pa_OpenStream().
On the PC, the user can specify a default device by
setting an environment variable. For example, to use device #1.
set PA_RECOMMENDED_OUTPUT_DEVICE=1
The user should first determine the available device ID by using
the supplied application "pa_devs".
*/
DLL_API PaDeviceID Pa_GetDefaultInputDeviceID( void );
DLL_API PaDeviceID Pa_GetDefaultOutputDeviceID( void );
/*
PaTimestamp is used to represent a continuous sample clock with arbitrary
start time useful for syncronisation. The type is used in the outTime
argument to the callback function and the result of Pa_StreamTime()
*/
typedef double PaTimestamp;
/*
Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure
referring to the device specified by id.
If id is out of range the function returns NULL.
The returned structure is owned by the PortAudio implementation and must
not be manipulated or freed. The pointer is guaranteed to be valid until
between calls to Pa_Initialize() and Pa_Terminate().
*/
DLL_API const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id );
/*
PortAudioCallback is implemented by clients of the portable audio api.
inputBuffer and outputBuffer are arrays of interleaved samples,
the format, packing and number of channels used by the buffers are
determined by parameters to Pa_OpenStream() (see below).
framesPerBuffer is the number of sample frames to be processed by the callback.
outTime is the time in samples when the buffer(s) processed by
this callback will begin being played at the audio output.
See also Pa_StreamTime()
userData is the value of a user supplied pointer passed to Pa_OpenStream()
intended for storing synthesis data etc.
return value:
The callback can return a nonzero value to stop the stream. This may be
useful in applications such as soundfile players where a specific duration
of output is required. However, it is not necessary to utilise this mechanism
as StopStream() will also terminate the stream. A callback returning a
nonzero value must fill the entire outputBuffer.
NOTE: None of the other stream functions may be called from within the
callback function except for Pa_GetCPULoad().
*/
typedef int (PortAudioCallback)(
void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/*
Stream flags
These flags may be supplied (ored together) in the streamFlags argument to
the Pa_OpenStream() function.
[ suggestions? ]
*/
#define paNoFlag (0)
#define paClipOff (1<<0) /* disable defult clipping of out of range samples */
#define paDitherOff (1<<1) /* disable default dithering */
#define paPlatformSpecificFlags (0x00010000)
typedef unsigned long PaStreamFlags;
/*
A single PortAudioStream provides multiple channels of real-time
input and output audio streaming to a client application.
Pointers to PortAudioStream objects are passed between PortAudio functions.
*/
typedef void PortAudioStream;
#define PaStream PortAudioStream
/*
Pa_OpenStream() opens a stream for either input, output or both.
stream is the address of a PortAudioStream pointer which will receive
a pointer to the newly opened stream.
inputDevice is the id of the device used for input (see PaDeviceID above.)
inputDevice may be paNoDevice to indicate that an input device is not required.
numInputChannels is the number of channels of sound to be delivered to the
callback. It can range from 1 to the value of maxInputChannels in the
device input record for the device specified in the inputDevice parameter.
If inputDevice is paNoDevice numInputChannels is ignored.
inputSampleFormat is the format of inputBuffer provided to the callback
function. inputSampleFormat may be any of the formats described by the
PaSampleFormat enumeration (see above). PortAudio guarantees support for
the sound devices native formats (nativeSampleFormats in the device info
record) and additionally 16 and 32 bit integer and 32 bit floating point
formats. Support for other formats is implementation defined.
inputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
inputDriverInfo is never required for correct operation. If not used
inputDriverInfo should be NULL.
outputDevice is the id of the device used for output (see PaDeviceID above.)
outputDevice may be paNoDevice to indicate that an output device is not required.
numOutputChannels is the number of channels of sound to be supplied by the
callback. See the definition of numInputChannels above for more details.
outputSampleFormat is the sample format of the outputBuffer filled by the
callback function. See the definition of inputSampleFormat above for more
details.
outputDriverInfo is a pointer to an optional driver specific data structure
containing additional information for device setup or stream processing.
outputDriverInfo is never required for correct operation. If not used
outputDriverInfo should be NULL.
sampleRate is the desired sampleRate for input and output
framesPerBuffer is the length in sample frames of all internal sample buffers
used for communication with platform specific audio routines. Wherever
possible this corresponds to the framesPerBuffer parameter passed to the
callback function.
numberOfBuffers is the number of buffers used for multibuffered
communication with the platform specific audio routines. This parameter is
provided only as a guide - and does not imply that an implementation must
use multibuffered i/o when reliable double buffering is available (such as
SndPlayDoubleBuffer() on the Macintosh.)
streamFlags may contain a combination of flags ORed together.
These flags modify the behavior of the
streaming process. Some flags may only be relevant to certain buffer formats.
callback is a pointer to a client supplied function that is responsible
for processing and filling input and output buffers (see above for details.)
userData is a client supplied pointer which is passed to the callback
function. It could for example, contain a pointer to instance data necessary
for processing the audio buffers.
return value:
Apon success Pa_OpenStream() returns PaNoError and places a pointer to a
valid PortAudioStream in the stream argument. The stream is inactive (stopped).
If a call to Pa_OpenStream() fails a nonzero error code is returned (see
PAError above) and the value of stream is invalid.
*/
DLL_API PaError Pa_OpenStream( PortAudioStream** stream,
PaDeviceID inputDevice,
int numInputChannels,
PaSampleFormat inputSampleFormat,
void *inputDriverInfo,
PaDeviceID outputDevice,
int numOutputChannels,
PaSampleFormat outputSampleFormat,
void *outputDriverInfo,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PaStreamFlags streamFlags,
PortAudioCallback *callback,
void *userData );
/*
Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that
opens the default input and/or ouput devices. Most parameters have
identical meaning to their Pa_OpenStream() counterparts, with the following
exceptions:
If either numInputChannels or numOutputChannels is 0 the respective device
is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() )
sampleFormat applies to both the input and output buffers.
*/
DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream,
int numInputChannels,
int numOutputChannels,
PaSampleFormat sampleFormat,
double sampleRate,
unsigned long framesPerBuffer,
unsigned long numberOfBuffers,
PortAudioCallback *callback,
void *userData );
/*
Pa_CloseStream() closes an audio stream, flushing any pending buffers.
*/
DLL_API PaError Pa_CloseStream( PortAudioStream* );
/*
Pa_StartStream() and Pa_StopStream() begin and terminate audio processing.
When Pa_StopStream() returns, all pending audio buffers have been played.
Pa_AbortStream() stops playing immediately without waiting for pending
buffers to complete.
*/
DLL_API PaError Pa_StartStream( PortAudioStream *stream );
DLL_API PaError Pa_StopStream( PortAudioStream *stream );
DLL_API PaError Pa_AbortStream( PortAudioStream *stream );
/*
Pa_StreamActive() returns one when the stream is playing audio,
zero when not playing, or a negative error number if the
stream is invalid.
The stream is active between calls to Pa_StartStream() and Pa_StopStream(),
but may also become inactive if the callback returns a non-zero value.
In the latter case, the stream is considered inactive after the last
buffer has finished playing.
*/
DLL_API PaError Pa_StreamActive( PortAudioStream *stream );
/*
Pa_StreamTime() returns the current output time for the stream in samples.
This time may be used as a time reference (for example syncronising audio to
MIDI).
*/
DLL_API PaTimestamp Pa_StreamTime( PortAudioStream *stream );
/*
The "CPU Load" is a fraction of total CPU time consumed by the
stream's audio processing.
A value of 0.5 would imply that PortAudio and the sound generating
callback was consuming roughly 50% of the available CPU time.
This function may be called from the callback function or the application.
*/
DLL_API double Pa_GetCPULoad( PortAudioStream* stream );
/*
Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for
the current host based on minimum latency.
On the PC, for the DirectSound implementation, latency can be optionally set
by user by setting an environment variable.
For example, to set latency to 200 msec, put:
set PA_MIN_LATENCY_MSEC=200
in the AUTOEXEC.BAT file and reboot.
If the environment variable is not set, then the latency will be determined
based on the OS. Windows NT has higher latency than Win95.
*/
DLL_API int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate );
/*
Sleep for at least 'msec' milliseconds.
You may sleep longer than the requested time so don't rely
on this for accurate musical timing.
*/
DLL_API void Pa_Sleep( long msec );
/*
Return size in bytes of a single sample in a given PaSampleFormat
or paSampleFormatNotSupported.
*/
DLL_API PaError Pa_GetSampleSize( PaSampleFormat format );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PORT_AUDIO_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,34 @@
Notes on Core Audio Implementation of PortAudio
by Phil Burk and Darren Gibbs
Document last updated March 20, 2002
WHAT WORKS
Output with very low latency, <10 msec.
Half duplex input or output.
Full duplex on the same CoreAudio device.
The paFLoat32, paInt16, paInt8, paUInt8 sample formats.
Pa_GetCPULoad()
Pa_StreamTime()
KNOWN BUGS OR LIMITATIONS
We do not yet support simultaneous input and output on different
devices. Note that some CoreAudio devices like the Roland UH30 look
like one device but are actually two different CoreAudio devices. The
BuiltIn audio is typically one CoreAudio device.
Mono doesn't work.
DEVICE MAPPING
CoreAudio devices can support both input and output. But the sample
rates supported may be different. So we have map one or two PortAudio
device to each CoreAudio device depending on whether it supports
input, output or both.
When we query devices, we first get a list of CoreAudio devices. Then
we scan the list and add a PortAudio device for each CoreAudio device
that supports input. Then we make a scan for output devices.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,51 @@
# Make PortAudio for Silicon Graphics IRIX (6.2)
# Pieter suurmond, september 23, 2001. (pa_sgi sub-version #0.21 for PA v15.)
# Based on SGI-specific sproc()-method to spawn children, not on POSIX-threads.
# Instead of "-lpthread", as with linux,
# just the audio- and math-library for SGI:
LIBS = -laudio -lm
CDEFINES = -I../pa_common
# Possible CFLAGS with MIPSpro compiler are: -32, -o32, -n32, -64,
# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging.
# And use for example -O2 or -O3 for better optimization:
CFLAGS = -O2
PASRC = ../pa_common/pa_lib.c pa_sgi.c
PAINC = ../pa_common/portaudio.h
# Tests that work (SGI Indy with R5000 @ 180MHz running IRIX 6.2).
TESTC = $(PASRC) ../pa_tests/patest_record.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_many.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_latency.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_longsine.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_saw.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_wire.c # OK
#TESTC = $(PASRC) ../pa_tests/pa_devs.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_sine.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_sine8.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_pink.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_clip.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_stop.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_dither.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_sync.c # BEEPS and delta's, no crashes anymore.
# Tests that were not yet performed.
#TESTC = $(PASRC) ../pa_tests/paqa_devs.c # test??
#TESTC = $(PASRC) ../pa_tests/paqa_errs.c # test??
#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c # test??
TESTH = $(PAINC)
all: patest
# "cc" for the MIPSpro compiler, may be changed to "gcc":
patest: $(TESTC) $(TESTH) Makefile
cc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
run: patest
./patest

View file

@ -0,0 +1,999 @@
/*
* $Id$
* PortAudio Portable Real-Time Audio Library. Copyright (c) 1999-2001 Phil Burk.
* Latest Version at: http://www.portaudio.com
*
* Silicon Graphics IRIX implementation by Pieter Suurmond, september 23, 2001.
* pa_sgi-sub-version number 0.21, for PortAudio v15.
* This implementation uses the sproc()-method, not the POSIX method.
*
* 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.
*
Modfication History:
8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2.
8/17/2001 - v15 pa_sgi sub-version #0.04 (unstable alpha release) Sent to Phil & Ross.
9/23/2001 - #0.21 Many fixes and changes: POLLIN for input, not POLLOUT.
Open and close ALports in the audio-process-thread.
Proper semaphore communication now.
Hopefully stable enough now (for beta release?).
TODO:
- Test under IRIX 6.5.
- Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion),
and maybe also the other natively supported formats? (might increase performance)
- The minimal number of buffers setting... I do not yet fully understand it.. I now just take *4.
REFERENCES:
- IRIX 6.2 man pages regarding SGI AL library.
- IRIS Digital MediaProgramming Guide (online books and man-pages come
with IRIX 6.2 and may not be publically available on the internet).
*/
#include <stdio.h> /* Standard libraries. */
#include <stdlib.h>
#include "../pa_common/portaudio.h" /* BETTER PATH !!!???? Portaudio headers. */
#include "../pa_common/pa_host.h"
#include "../pa_common/pa_trace.h"
#include <sys/time.h>
#include <sys/types.h>
#include <sys/prctl.h>
#include <sys/schedctl.h> /* For schedctl(NDPRI, NDPHIMIN). */
#include <fcntl.h> /* fcntl.h needed. */
#include <unistd.h> /* For streams, ioctl(), etc. */
#include <ulocks.h>
#include <poll.h>
#include <dmedia/audio.h> /* System specific (IRIX 6.2). */
/*--------------------------------------------*/
#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x) /* PRINT(x) */
#define DBUGX(x) /* PRINT(x) */
#define MAX_CHARS_DEVNAME (16) /* Was 32 in OSS (20 for AL but "in"/"out" is concat. */
#define MAX_SAMPLE_RATES (8) /* Known from SGI AL there are 7 (was 10 in OSS v15). */
typedef struct internalPortAudioDevice /* IRIX specific device info: */
{
PaDeviceID /* NEW: */ pad_DeviceID; /* THIS "ID" IS NEW HERE (Pieter)! */
long pad_ALdevice; /* SGI-number! */
double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
char pad_DeviceName[MAX_CHARS_DEVNAME+1]; /* +1 for \0, one more than OSS. */
PaDeviceInfo pad_Info; /* pad_Info (v15) contains: */
/* int structVersion; */
/* const char* name; */
/* int maxInputChannels, maxOutputChannels; */
/* int numSampleRates; Num rates, or -1 if range supprtd. */
/* const double* sampleRates; Array of supported sample rates, */
/* PaSampleFormat nativeSampleFormats; or {min,max} if range supported. */
struct internalPortAudioDevice* pad_Next; /* Singly linked list, (NULL=end). */
} internalPortAudioDevice;
typedef struct PaHostSoundControl /* Structure to contain all SGI IRIX specific data. */
{
ALconfig pahsc_ALconfigIN, /* IRIX-audio-library-datatype. Configuration */
pahsc_ALconfigOUT; /* stucts separate for input and output ports. */
ALport pahsc_ALportIN, /* IRIX-audio-library-datatype. ALports can only be */
pahsc_ALportOUT; /* unidirectional, so we sometimes need 2 of them. */
int pahsc_threadPID; /* Sproc()-result, written by PaHost_StartEngine(). */
short *pahsc_NativeInputBuffer, /* Allocated here, in this file, if necessary. */
*pahsc_NativeOutputBuffer;
unsigned int pahsc_BytesPerInputBuffer, /* Native buffer sizes in bytes, really needed here */
pahsc_BytesPerOutputBuffer; /* to free FAST memory, if buffs were alloctd FAST. */
unsigned int pahsc_SamplesPerInputBuffer, /* These amounts are needed again and again in the */
pahsc_SamplesPerOutputBuffer; /* audio-thread (don't need to be kept globally). */
struct itimerval pahsc_EntryTime, /* For measuring CPU utilization (same as linux). */
pahsc_LastExitTime;
long pahsc_InsideCountSum,
pahsc_TotalCountSum;
} PaHostSoundControl;
/*-------------------------------------------------------- Shared Data -------------------------------*/
static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */
static int sPaHostError = 0; /* Maybe more than one process writing errs!? */
usema_t *SendSema, /* These variables are shared between the */
*RcvSema; /* audio handling process and main process. */
/*--------------------------*/
long Pa_GetHostError(void)
{
return (long)sPaHostError;
}
/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/
/* (copied from source pa_linux_oss/pa_linux_oss.c) */
static void Pa_StartUsageCalculation( internalPortAudioStream *past )
{
struct itimerval itimer;
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return;
/* Query system timer for usage analysis and to prevent overuse of CPU. */
getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime );
}
static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB )
{
long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec;
long usecs = secs * 1000000;
usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec);
return usecs;
}
static void Pa_EndUsageCalculation( internalPortAudioStream *past )
{
struct itimerval currentTime;
long insideCount;
long totalCount; /* Measure CPU utilization during this callback. */
#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 (getitimer( ITIMER_REAL, &currentTime ) == 0 )
{
if (past->past_IfLastExitValid)
{
insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, &currentTime );
pahsc->pahsc_InsideCountSum += insideCount;
totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, &currentTime );
pahsc->pahsc_TotalCountSum += totalCount;
/* DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */
/* Low pass filter the result because sometimes we get called several times in a row. */
/* That can cause the TotalCount to be very low which can cause the usage to appear */
/* unnaturally high. So we must filter numerator and denominator separately!!! */
if (pahsc->pahsc_InsideCountSum > 0)
{
past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
(LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum));
past->past_AverageTotalCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
(LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum));
past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
pahsc->pahsc_InsideCountSum = 0;
pahsc->pahsc_TotalCountSum = 0;
}
}
past->past_IfLastExitValid = 1;
}
pahsc->pahsc_LastExitTime.it_value.tv_sec = 100;
pahsc->pahsc_LastExitTime.it_value.tv_usec = 0;
setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL );
past->past_IfLastExitValid = 1;
} /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/
/*--------------------------------------------------------------------------------------*/
PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */
{ /* call to report via ERR_RPT(), yields a PaError-num. */
const char* a = "SGI AL "; /* (Not absolutely sure errno came from THIS thread! */
switch(oserror()) /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */
{
case AL_BAD_OUT_OF_MEM:
ERR_RPT(("%sout of memory.\n", a));
return paInsufficientMemory; /* Known PaError. */
case AL_BAD_CONFIG:
ERR_RPT(("%sconfiguration invalid or NULL.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_CHANNELS:
ERR_RPT(("%schannels not 1,2 or 4.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_NO_PORTS:
ERR_RPT(("%sout of audio ports.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_DEVICE:
ERR_RPT(("%swrong device number.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_DEVICE_ACCESS:
ERR_RPT(("%swrong device access.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_DIRECTION:
ERR_RPT(("%sinvalid direction.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_SAMPFMT:
ERR_RPT(("%sdoesn't accept sampleformat.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_FLOATMAX:
ERR_RPT(("%smax float value is zero.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_WIDTH:
ERR_RPT(("%sunsupported samplewidth.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_QSIZE:
ERR_RPT(("%sinvalid queue size.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_PVBUFFER:
ERR_RPT(("%sPVbuffer null.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_BUFFERLENGTH_NEG:
ERR_RPT(("%snegative bufferlength.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_BUFFERLENGTH_ODD:
ERR_RPT(("%sodd bufferlength.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_PARAM:
ERR_RPT(("%sparameter not valid for device.\n", a));
return paHostError; /* Generic PaError. */
default:
ERR_RPT(("%sunknown error.\n", a));
return paHostError; /* Generic PaError. */
}
}
/*------------------------------------------------------------------------------------------*/
/* Tries to set various rates and formats and fill in the device info structure. */
static PaError Pa_sgiQueryDevice(long ALdev, /* (AL_DEFAULT_DEVICE) */
PaDeviceID id, /* (DefaultI|ODeviceID()) */
char* name, /* (for example "SGI AL") */
internalPortAudioDevice* pad) /* Result written to pad. */
{
int format;
long min, max; /* To catch hardware characteristics. */
ALseterrorhandler(0); /* 0 = turn off the default error handler. */
/*--------------------------------------------------------------------------------------*/
pad->pad_ALdevice = ALdev; /* Set the AL device number. */
pad->pad_DeviceID = id; /* Set the PA device number. */
if (strlen(name) > MAX_CHARS_DEVNAME) /* MAX_CHARS defined above. */
{
ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name));
return paHostError;
}
strcpy(pad->pad_DeviceName, name); /* Write name-string. */
pad->pad_Info.name = pad->pad_DeviceName; /* Set pointer,..hmmm. */
/*--------------------------------- natively supported sample formats: -----------------*/
pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */
/* Then also choose other CallConvertXX()! */
/*--------------------------------- number of available i/o channels: ------------------*/
if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max))
return translateSGIerror();
pad->pad_Info.maxInputChannels = max;
DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels))
if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max))
return translateSGIerror();
pad->pad_Info.maxOutputChannels = max;
DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels))
/*--------------------------------- supported samplerates: ----------------------*/
pad->pad_Info.numSampleRates = 7;
pad->pad_Info.sampleRates = pad->pad_SampleRates;
pad->pad_SampleRates[0] = (double)AL_RATE_8000; /* long -> double. */
pad->pad_SampleRates[1] = (double)AL_RATE_11025;
pad->pad_SampleRates[2] = (double)AL_RATE_16000;
pad->pad_SampleRates[3] = (double)AL_RATE_22050;
pad->pad_SampleRates[4] = (double)AL_RATE_32000;
pad->pad_SampleRates[5] = (double)AL_RATE_44100;
pad->pad_SampleRates[6] = (double)AL_RATE_48000;
if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max)) /* Ask INPUT rate-max. */
return translateSGIerror(); /* double -> long. */
if (max != (long)(0.5 + pad->pad_SampleRates[6])) /* FP-compare not recommndd. */
goto weird;
if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max. */
return translateSGIerror();
if (max != (long)(0.5 + pad->pad_SampleRates[6]))
{
weird: ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max));
return paHostError; /* Or make it a warning and just carry on... */
}
/*-------------------------------------------------------------------------------*/
return paNoError;
}
/*--------------------------------------------------------------------------------*/
int Pa_CountDevices() /* Name of this function suggests it only counts and */
{ /* is NOT destructive, it however resets whole PA ! */
int numDevices = 0; /* Let 's not do that here. */
internalPortAudioDevice* currentDevice = sDeviceList; /* COPY GLOBAL VAR. */
#if 0 /* Remains from linux_oss v15: Pa_Initialize(), on */
if (!currentDevice) /* its turn, calls PaHost_Init() via file pa_lib.c. */
Pa_Initialize(); /* Isn't that a bit too 'rude'? Don't be too */
#endif /* friendly to clients that forgot to initialize PA. */
while (currentDevice) /* Slower but more elegant than the sNumDevices-way: */
{
numDevices++;
currentDevice = currentDevice->pad_Next;
}
return numDevices;
}
/*-------------------------------------------------------------------------------*/
static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id)
{
int numDevices = 0;
internalPortAudioDevice *res = (internalPortAudioDevice*)NULL;
internalPortAudioDevice *pad = sDeviceList; /* COPY GLOBAL VAR. */
while (pad) /* pad may be NULL, that's ok, return 0. */
{ /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.) */
if (pad->pad_DeviceID == id) /* This the device we were looking for? */
res = pad; /* But keep on(!) counting so we don't */
numDevices++; /* have to call Pa_CountDevices() later. */
pad = pad->pad_Next; /* Advance to the next device or NULL. */
} /* No assumptions about order of ID's in */
if (!res) /* the list. */
ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id));
if ((id < 0) || (id >= numDevices))
{
ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id));
#if 1 /* Be strict, even when found, */
res = (internalPortAudioDevice*)NULL; /* do not accept illegal ID's. */
#endif
}
return res;
}
/*----------------------------------------------------------------------*/
const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id)
{
PaDeviceInfo* res = (PaDeviceInfo*)NULL;
internalPortAudioDevice* pad = Pa_GetInternalDevice(id); /* Call. */
if (pad)
res = &pad->pad_Info; /* Not finding the specified ID is not */
if (!res) /* the same as &pad->pad_Info == NULL. */
ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id));
return res; /* So (maybe) a second/third ERR_RPT(). */
}
/*------------------------------------------------*/
PaDeviceID Pa_GetDefaultInputDeviceID(void)
{
return 0; /* 0 is the default device ID. */
}
/*------------------------------------------------*/
PaDeviceID Pa_GetDefaultOutputDeviceID(void)
{
return 0;
}
/*-------------------------------------------------------------------------------------------------*/
/* Build linked a list with all the available audio devices on this SGI machine (only 1 for now). */
PaError PaHost_Init(void) /* Called by Pa_Initialize() from pa_lib.c. */
{
internalPortAudioDevice* pad;
PaError r = paNoError;
int audioLibFileID; /* To test for the presence of audio. */
if (sDeviceList) /* Allow re-init, only warn, no error. */
{
ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n"));
return r;
}
/*------------- ADD THE SGI DEFAULT DEVICE TO THE LIST: ---------------------------------------*/
audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY); /* Try to open Indigo style audio */
if (audioLibFileID < 0) /* IO port. On failure, machine */
{ /* has no audio ability. */
ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n"));
return paHostError;
}
close(audioLibFileID); /* Allocate fast mem to hold device info. */
pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice));
if (pad == NULL)
return paInsufficientMemory;
memset(pad, 0, sizeof(internalPortAudioDevice)); /* "pad->pad_Next = NULL" is more elegant. */
r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE, /* Set AL device num (AL_DEFAULT_DEVICE). */
Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */
"AL default", /* A suitable name. */
pad); /* Write args and queried info into pad. */
if (r != paNoError)
{
ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r));
PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); /* sDeviceList still NULL ! */
}
else
sDeviceList = pad; /* First element in linked list. pad->pad_Next already NULL. */
/*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LINKED LIST: -------------------*/
/*---------------------------------------------------------------------------------------------*/
return r;
}
/*--------------------------------------------------------------------------------------------*/
#define MIN(a,b) ((a)<(b)?(a):(b)) /* MIN()-function is used below. */
#define kPollSEMA 0 /* To index the pollfd-array, reads nicer than just */
#define kPollOUT 1 /* numbers. */
#define kPollIN 2
void Pa_SgiAudioProcess(void *v) /* This function is sproc-ed by PaHost_StartEngine() */
{ /* as a separate thread. (Argument must be void*). */
short evtLoop; /* Reset by parent indirectly, or at local errors. */
PaError result;
struct pollfd PollFD[3]; /* To catch kPollSEMA-, kPollOUT- and kPollIN-events. */
internalPortAudioStream *past = (internalPortAudioStream*)v; /* Copy void-ptr-argument.*/
PaHostSoundControl *pahsc;
short inputEvent, outputEvent, /* .revents members are of type short. */
semaEvent = 0;
DBUG(("Entering sproc-thread.\n"));
if (!past)
{
sPaHostError = paInternalError; /* Or paBadStreamPtr ? */
ERR_RPT(("argument NULL!\n"));
goto skip;
}
pahsc = (PaHostSoundControl*)past->past_DeviceData;
if (!pahsc)
{
sPaHostError = paInternalError; /* The only way is to signal error to shared area?! */
ERR_RPT(("past_DeviceData NULL!\n"));
goto skip; /* Sproc-ed threads MAY NOT RETURN paInternalError. */
}
/*----------------------------- open AL-ports here, after sproc(): -----------------------*/
if (past->past_NumInputChannels > 0) /* Open input port. */
{
pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", pahsc->pahsc_ALconfigIN);
if (!pahsc->pahsc_ALportIN)
{
ERR_RPT(("Failed to open AL input port.\n"));
sPaHostError = paInternalError;
goto skip;
}
DBUG(("Opened %d input channel(s).\n", past->past_NumInputChannels));
}
if (past->past_NumOutputChannels > 0) /* Open output port. */
{
pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", pahsc->pahsc_ALconfigOUT);
if (!pahsc->pahsc_ALportOUT)
{
ERR_RPT(("Failed to open AL output port.\n"));
sPaHostError = paInternalError; /* Assume pahsc_ALconfigs are the */
goto skip; /* same for IN and OUT in case */
} /* both ports are opened (bidir). */
DBUG(("Opened %d output channel(s).\n", past->past_NumOutputChannels));
}
/*-----------------------------------------------------------------------*/
past->past_IsActive = 1; /* Wasn't this already done by the calling parent?! */
PollFD[kPollIN].fd = ALgetfd(pahsc->pahsc_ALportIN); /* ALgetfd returns -1 on failures */
PollFD[kPollIN].events = POLLIN; /* such as ALport not there. */
PollFD[kPollOUT].fd = ALgetfd(pahsc->pahsc_ALportOUT);
PollFD[kPollOUT].events = POLLOUT; /* .events = POLLOUT is OK. */
schedctl(NDPRI, NDPHIMIN); /* Sets non-degrading priority for this process. */
PollFD[kPollSEMA].fd = usopenpollsema(SendSema, 0777); /* To communicate with parent. */
PollFD[kPollSEMA].events = POLLIN; /* .events = POLLIN is OK. */
uspsema(SendSema); /* Blocks until ... MUST be here, this uspsema(). */
evtLoop = ((past->past_StopNow | past->past_StopSoon) == 0);
while (evtLoop)
{
/*---------------------------- SET FILLPOINTS AND WAIT UNTIL SOMETHING HAPPENS: ----------*/
if (pahsc->pahsc_NativeInputBuffer) /* Then pahsc_ALportIN should also be there! */
/* For input port, fill point is number of locations in the sample queue that must be */
/* filled in order to trigger a return from select(). (or poll()) */
/* Notice IRIX docs mention number of samples as argument, not number of sampleframes.*/
if (ALsetfillpoint(pahsc->pahsc_ALportIN, pahsc->pahsc_SamplesPerInputBuffer))
{ /* Same amount as transferred per time. */
ERR_RPT(("ALsetfillpoint() for ALportIN failed.\n"));
sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */
goto skip;
}
if (pahsc->pahsc_NativeOutputBuffer) /* Then pahsc_ALportOUT should also be there! */
/* For output port, fill point is number of locations that must be free in order to */
/* wake up from select(). (or poll()) */
if (ALsetfillpoint(pahsc->pahsc_ALportOUT, pahsc->pahsc_SamplesPerOutputBuffer))
{
ERR_RPT(("ALsetfillpoint() for ALportOUT failed.\n"));
sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */
goto skip;
} /* poll() with timeout=-1 makes it block until a requested */
poll(PollFD, 3, -1); /* event occurs or until call is interrupted. If fd-value in */
/* array <0, events is ignored and revents is set to 0. */
/*---------------------------- MESSAGE-EVENT FROM PARENT THREAD: -------------------------*/
semaEvent = PollFD[kPollSEMA].revents & POLLIN;
if (semaEvent)
{
if (past->past_StopSoon)
evtLoop = 0;
if (past->past_StopNow)
goto skip;
}
/*------------------------------------- FILLED-EVENT FROM INPUT BUFFER: --------------------------*/
inputEvent = PollFD[kPollIN].revents & POLLIN;
if (inputEvent) /* Don't need to check (pahsc->pahsc_NativeInputBuffer): */
{ /* if buffer was not there, ALport not there, no events! */
if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_NativeInputBuffer,
pahsc->pahsc_SamplesPerInputBuffer))
{ /* Here again: number of samples instead of number of frames. */
ERR_RPT(("ALreadsamps() failed.\n"));
sPaHostError = paInternalError;
goto skip;
}
}
outputEvent = PollFD[kPollOUT].revents & POLLOUT;
/*------------------------------------- USER-CALLBACK-ROUTINE: -----------------------------------*/
if (inputEvent | outputEvent) /* (Bitwise is ok.) */
{ /* To be sure we that really DID input-transfer or gonna DO output-transfer, and that it is */
/* not just "sema"- (i.e. user)-event, or some other system-event that awakened the poll(). */
Pa_StartUsageCalculation(past); /* Convert 16 bit native data to */
result = Pa_CallConvertInt16(past, /* user data and call user routine. */
pahsc->pahsc_NativeInputBuffer,
pahsc->pahsc_NativeOutputBuffer);
Pa_EndUsageCalculation(past);
if (result)
{
DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result));
goto skip; /* This is apparently NOT an error! */
} /* Just letting the userCallBack stop us. */
}
/*------------------------------------- FREE-EVENT FROM OUTPUT BUFFER: ---------------------------*/
if (outputEvent) /* Don't need to check (pahsc->pahsc_NativeOutputBuffer) */
{ /* because if filedescriptor not there, no event for it. */
if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_NativeOutputBuffer,
pahsc->pahsc_SamplesPerOutputBuffer))
{
ERR_RPT(("ALwritesamps() failed.\n")); /* Better use SEMAS for messaging back to parent! */
sPaHostError = paInternalError;
goto skip;
}
}
}
skip:
/*------------------------------- close AL-ports ----------------------------*/
if (pahsc->pahsc_ALportIN)
{
if (ALcloseport(pahsc->pahsc_ALportIN))
translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
else /* But go on anyway... to release other stuff... */
pahsc->pahsc_ALportIN = (ALport)0;
}
if (pahsc->pahsc_ALportOUT)
{
if (ALcloseport(pahsc->pahsc_ALportOUT))
translateSGIerror();
else
pahsc->pahsc_ALportOUT = (ALport)0;
}
past->past_IsActive = 0;
if (semaEvent)
{
uspsema(SendSema); /* StopEngine() was still waiting for this acknowledgement. */
usvsema(RcvSema); /* (semaEvent initialized with 0.) */
}
DBUG(("Leaving sproc-thread.\n"));
}
#define kALinternalQueuesizeFact 4L /* Internal queue 4 times as large as transferSize. */
/* Used below, twice: for input and for output. */
/*--------------------------------------------------------------------------------------*/
PaError PaHost_OpenStream(internalPortAudioStream *past)
{
PaError result = paNoError;
PaHostSoundControl *pahsc;
unsigned int minNumBuffers;
internalPortAudioDevice *padIN, *padOUT; /* For looking up native AL-numbers. */
long pvbuf[8]; /* To get/set hardware configs. */
long sr;
DBUG(("PaHost_OpenStream() called.\n")); /* Alloc FASTMEM and init host data. */
if (!past)
{
ERR_RPT(("Streampointer NULL!\n"));
result = paBadStreamPtr; goto done;
}
pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
if (pahsc == NULL)
{
ERR_RPT(("FAST Memory allocation failed.\n")); /* Pass trough some ERR_RPT-exit- */
result = paInsufficientMemory; goto done; /* code (nothing will be freed). */
}
memset(pahsc, 0, sizeof(PaHostSoundControl));
pahsc->pahsc_threadPID = -1; /* Should pahsc_threadPID be inited to */
past->past_DeviceData = (void*)pahsc; /* -1 instead of 0 ?? */
/*------------------------------------------ Manipulate hardware if necessary and allowed: --*/
ALseterrorhandler(0); /* 0 = turn off the default error handler. */
pvbuf[0] = AL_INPUT_RATE;
pvbuf[2] = AL_INPUT_COUNT;
pvbuf[4] = AL_OUTPUT_RATE; /* TO FIX: rates may be logically, not always in Hz! */
pvbuf[6] = AL_OUTPUT_COUNT;
sr = (long)(past->past_SampleRate + 0.5); /* Common for input and output :-) */
/*---------------------------------------------------- SET INPUT CONFIGURATION: ------------------------*/
if (past->past_NumInputChannels > 0) /* We need to lookup the corre- */
{ /* sponding native AL-number(s). */
/*--------------------------------------------------- Allocate native buffers: --------*/
pahsc->pahsc_SamplesPerInputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
past->past_NumInputChannels; /* audio-thread. */
pahsc->pahsc_BytesPerInputBuffer = pahsc->pahsc_SamplesPerInputBuffer * sizeof(short);
pahsc->pahsc_NativeInputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputBuffer);
if (!pahsc->pahsc_NativeInputBuffer)
{
ERR_RPT(("Fast memory allocation failed (in).\n"));
result = paInsufficientMemory;
goto done;
}
padIN = Pa_GetInternalDevice(past->past_InputDeviceID);
if (!padIN)
{
ERR_RPT(("Pa_GetInternalDevice() for input failed.\n"));
result = paHostError;
goto done;
}
if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */
goto sgiError; /* the same AL-device, the AL-library might */
if (pvbuf[1] != sr) /* contain more than AL_DEFAULT_DEVICE in */
{ /* Rate different from current harware-rate? the future. Therefore 2 seperate queries. */
if (pvbuf[3] > 0) /* Means, there's other clients using AL-input-ports */
{
ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \
another process is currently using input at %ld kHz.\n", sr, pvbuf[1]));
result = paHostError;
goto done;
}
pvbuf[1] = sr; /* Then set input-rate. */
if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2))
goto sgiError; /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */
} /* IS NOT CHECKED, AT LEAST NOT BY ME, WITHIN THIS FILE! Does PA do? */
pahsc->pahsc_ALconfigIN = ALnewconfig(); /* Released at PaHost_CloseStream(). */
if (pahsc->pahsc_ALconfigIN == (ALconfig)0)
goto sgiError;
if (ALsetsampfmt(pahsc->pahsc_ALconfigIN, AL_SAMPFMT_TWOSCOMP))/* Choose paInt16 as native i/o-format. */
goto sgiError;
if (ALsetwidth (pahsc->pahsc_ALconfigIN, AL_SAMPLE_16)) /* Only meaningful when sample format for */
goto sgiError; /* config is set to two's complement format. */
/************************ Future versions might (dynamically) switch to 32-bit floats? *******
if (ALsetsampfmt(pahsc_ALconfigIN, AL_SAMPFMT_FLOAT)) (Then also call another CallConvert-func.)
goto sgiError;
if (ALsetfloatmax (pahsc_ALconfigIN, 1.0)) Only meaningful when sample format for config
goto sgiError; is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */
/*--------- Set internal AL queuesize (in samples) -------------------------------*/
if (ALsetqueuesize(pahsc->pahsc_ALconfigIN, (long)pahsc->pahsc_SamplesPerInputBuffer *
(long)kALinternalQueuesizeFact))
goto sgiError; /* Or should we use past_NumUserBuffers here? */
/* Do 4 timea, using 2 times may give glitches. */
if (ALsetchannels (pahsc->pahsc_ALconfigIN, (long)(past->past_NumInputChannels)))
goto sgiError; /* Returns 0 on success, -1 on failure. */
}
/*---------------------------------------------------- SET OUTPUT CONFIGURATION: ------------------------*/
if (past->past_NumOutputChannels > 0) /* CARE: padOUT/IN may NOT be NULL if Channels <= 0! */
{ /* We use padOUT/IN later on, or at least 1 of both. */
pahsc->pahsc_SamplesPerOutputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
past->past_NumOutputChannels; /* audio-thread. */
pahsc->pahsc_BytesPerOutputBuffer = pahsc->pahsc_SamplesPerOutputBuffer * sizeof(short);
pahsc->pahsc_NativeOutputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputBuffer);
if (!pahsc->pahsc_NativeOutputBuffer)
{
ERR_RPT(("Fast memory allocation failed (out).\n"));
result = paInsufficientMemory;
goto done;
}
padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID);
if (!padOUT)
{
ERR_RPT(("Pa_GetInternalDevice() for output failed.\n"));
result = paHostError;
goto done;
}
if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4))
goto sgiError;
if (pvbuf[5] != sr)
{ /* Output needed and rate different from current harware-rate. */
if (pvbuf[7] > 0) /* Means, there's other clients using AL-output-ports */
{
ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \
another process is currently using output at %ld kHz.\n", sr, pvbuf[5]));
result = paHostError;
goto done; /* Will free again the inputbuffer */
} /* that was just created above. */
pvbuf[5] = sr; /* Then set output-rate. */
if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2))
goto sgiError;
}
pahsc->pahsc_ALconfigOUT = ALnewconfig(); /* Released at PaHost_CloseStream(). */
if (pahsc->pahsc_ALconfigOUT == (ALconfig)0)
goto sgiError;
if (ALsetsampfmt(pahsc->pahsc_ALconfigOUT, AL_SAMPFMT_TWOSCOMP)) /* Choose paInt16 as native i/o-format. */
goto sgiError;
if (ALsetwidth (pahsc->pahsc_ALconfigOUT, AL_SAMPLE_16)) /* Only meaningful when sample format for */
goto sgiError; /* config is set to two's complement format. */
/************************************ Future versions might (dynamically) switch to 32-bit floats? *******/
if (ALsetqueuesize(pahsc->pahsc_ALconfigOUT, (long)pahsc->pahsc_SamplesPerOutputBuffer *
(long)kALinternalQueuesizeFact))
goto sgiError; /* Or should we use past_NumUserBuffers here?*/
if (ALsetchannels (pahsc->pahsc_ALconfigOUT, (long)(past->past_NumOutputChannels)))
goto sgiError;
}
/*---------- ?? --------------------*/
/* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer, past->past_SampleRate);
past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ?
minNumBuffers : past->past_NumUserBuffers; /* I don't yet use past_NumUserBuffers */
/*----------------------------------------------- TEST DEVICE ID's: --------------------*/
if ((past->past_OutputDeviceID != past->past_InputDeviceID) && /* Who SETS these devive-numbers? */
(past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0))
{
ERR_RPT(("Cannot setup bidirectional stream between different devices.\n"));
result = paHostError;
goto done;
}
goto done; /* (no errors occured) */
sgiError:
result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
done:
if (result != paNoError)
PaHost_CloseStream(past); /* Frees memory (only if really allocated!). */
return result;
}
/*-----------------------------------------------------*/
PaError PaHost_StartOutput(internalPortAudioStream *past)
{
return paNoError; /* Hmm, not implemented yet? */
}
PaError PaHost_StartInput(internalPortAudioStream *past)
{
return paNoError;
}
/*------------------------------------------------------------------------------*/
PaError PaHost_StartEngine(internalPortAudioStream *past)
{
PaHostSoundControl *pahsc;
usptr_t *arena;
if (!past) /* Test argument. */
{
ERR_RPT(("PaHost_StartEngine(NULL)!\n"));
return paBadStreamPtr;
}
pahsc = (PaHostSoundControl*)past->past_DeviceData;
if (!pahsc)
{
ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData = NULL!\n"));
return paHostError;
}
past->past_StopSoon = 0; /* Assume SGI ALport is already opened! */
past->past_StopNow = 0; /* Why don't we check pahsc for NULL? */
past->past_IsActive = 1;
/* Although the pthread_create() function, as well as <pthread.h>, may be */
/* available in IRIX, use sproc() on SGI to create audio-background-thread. */
/* (Linux/oss uses pthread_create() instead of __clone() because: */
/* - pthread_create also works for other UNIX systems like Solaris, */
/* - Java HotSpot VM crashes in pthread_setcanceltype() using __clone().) */
usconfig(CONF_ARENATYPE, US_SHAREDONLY); /* (From SGI-AL-examples, file */
arena = usinit(tmpnam(0)); /* motifexample.c, function */
SendSema = usnewpollsema(arena, 0); /* InitializeAudioProcess().) */
RcvSema = usnewsema(arena, 1); /* 1= common mutual exclusion semaphore, where 1 and only 1 process
will be permitted through a semaphore at a time. Values > 1
imply that up to val resources may be simultaneously used, but requests
for more than val resources cause the calling process to block until a
resource comes free (by a process holding a resource performing a
usvsema(). IS THIS usnewsema() TOO PLATFORM SPECIFIC? */
prctl(PR_SETEXITSIG, 0); /* No not (void*)9, but 0, which doesn't kill the parent! */
/* PR_SETEXITSIG controls whether all members of a share group will be
signaled if any one of them leaves the share group (either via exit()
or exec()). If 2nd arg, interpreted as an int is 0, then normal IRIX
process termination rules apply, namely that the parent is sent a
SIGCLD upon death of child, but no indication of death of parent is
given. If the second argument is a valid signal number then if any
member of a share group leaves the share group, a signal is
sent to ALL surviving members of the share group. */
/* SPAWN AUDIO-CHILD: */
pahsc->pahsc_threadPID = sproc(Pa_SgiAudioProcess, /* Returns process ID of */
PR_SALL, /* new process, or -1. */
(void*)past); /* Pass past as optional */ /* IS THIS SAFE, will past never */
if (pahsc->pahsc_threadPID == -1) /* third void-ptr-arg. */ /* be moved around in memory???? */
{
ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n"));
sPaHostError = oserror(); /* Pass native error-number to shared area. */
return paHostError; /* But return the generic error-number. */
}
return paNoError; /* Hmmm, errno may come from other threads in same group! */
} /* ("man sproc" in IRIX6.2 to read about _SGI_MP_SOURCE.) */
/*------------------------------------------------------------------------------*/
PaError PaHost_StopEngine(internalPortAudioStream *past, int abort)
{
int hres;
long timeOut;
PaError result = paNoError;
PaHostSoundControl *pahsc;
DBUG(("PaHost_StopEngine() called.\n"));
if (!past)
return paBadStreamPtr;
pahsc = (PaHostSoundControl*)past->past_DeviceData;
/* Prevent from doing this twice!! */
if ((!pahsc) || /* Some tests call this CLOSE twice!! */
(!past->past_IsActive) ||
past->past_StopSoon || past->past_StopNow)
return result; /* paNoError (already stopped, no err?). */
past->past_StopSoon = 1; /* Tell background thread to stop generating */
if (abort) /* more and to let current data play out. If */
past->past_StopNow = 1; /* aborting, tell backgrnd thread to stop NOW! */
/*---- USE SEMAPHORE LOCK TO COMMUNICATE: -----*/
usvsema(SendSema); /* Increments count associated with SendSema. */
/* Wait for the response. */
uspsema(RcvSema); /* Decrements count of previously allocated */
/* semaphore specified by RcvSema. */
while (past->past_IsActive) /* REALLY WAIT. */
{
/* DBUG(("wait 1 ms for audio-thread to stop.\n")); */
Pa_Sleep(1);
}
#if 0 /* We don't need to KILL(), just COMMUNICATE and be patient... */
if (pahsc->pahsc_threadPID != -1) /* Did we really init it to -1 somewhere? */
{
DBUG(("PaHost_StopEngine() is about to kill(SIGKILL) audio-thread.\n"));
if (kill(pahsc->pahsc_threadPID, SIGKILL)) /* Or SIGTERM or SIGQUIT(core) */
{ /* Returns -1 in case of error. */
result = paHostError;
sPaHostError = oserror(); /* Hmmm, other threads may also write here! */
ERR_RPT(("PaHost_StopEngine() failed to kill audio-thread.\n"));
}
else
pahsc->pahsc_threadPID = -1; /* Notify that we've killed this thread. */
}
#endif
past->past_IsActive = 0; /* Even when kill() failed and pahsc_threadPID still there??? */
return result;
}
/*---------------------------------------------------------------*/
PaError PaHost_StopOutput(internalPortAudioStream *past, int abort)
{
return paNoError; /* Not implemented yet? */
}
PaError PaHost_StopInput(internalPortAudioStream *past, int abort )
{
return paNoError;
}
/*******************************************************************/
PaError PaHost_CloseStream(internalPortAudioStream *past)
{
PaHostSoundControl *pahsc;
PaError result = paNoError;
DBUG(("PaHost_CloseStream() called.\n"));
if (!past)
return paBadStreamPtr;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if (!pahsc) /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */
return result; /* This test prevents from freeing NULL-pointers. */
if (pahsc->pahsc_ALconfigIN)
{ /* Release configuration structs, only if allocated. */
ALfreeconfig(pahsc->pahsc_ALconfigIN);
pahsc->pahsc_ALconfigIN = NULL;
}
if (pahsc->pahsc_ALconfigOUT)
{
ALfreeconfig(pahsc->pahsc_ALconfigOUT); /* (Al-ports were already closed by audioProcess). */
pahsc->pahsc_ALconfigOUT = NULL;
}
if (pahsc->pahsc_NativeInputBuffer)
{
PaHost_FreeFastMemory(pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_BytesPerInputBuffer);
pahsc->pahsc_NativeInputBuffer = NULL;
}
if (pahsc->pahsc_NativeOutputBuffer)
{
PaHost_FreeFastMemory(pahsc->pahsc_NativeOutputBuffer, pahsc->pahsc_BytesPerOutputBuffer);
pahsc->pahsc_NativeOutputBuffer = NULL;
}
PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl));
past->past_DeviceData = NULL; /* PaHost_OpenStream() allocated FAST MEM. */
return result;
}
/*************************************************************************
** Determine minimum number of buffers required for this host based
** on minimum latency. Latency can be optionally set by user by setting
** an environment variable. For example, to set latency to 200 msec, put:
** set PA_MIN_LATENCY_MSEC=200
** in the AUTOEXEC.BAT file and reboot.
** If the environment variable is not set, then the latency will be
** determined based on the OS. Windows NT has higher latency than Win95.
*/
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
{
return 2;
}
/* Hmmm, the note above isn't appropriate for SGI I'm afraid... */
/* Do we HAVE to do it this way under IRIX???.... */
/*--------------------------------------------------------------*/
/*---------------------------------------------------------------------*/
PaError PaHost_Term(void) /* Frees all of the linked audio-devices. */
{ /* Called by Pa_Terminate() from pa_lib.c. */
internalPortAudioDevice *pad = sDeviceList,
*nxt;
while (pad)
{
DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName));
nxt = pad->pad_Next;
PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));
pad = nxt; /* PaHost_Init allocated this FAST MEM.*/
}
sDeviceList = (internalPortAudioDevice*)NULL;
return 0; /* Got rid of sNumDevices=0; */
}
/***********************************************************************/
void Pa_Sleep( long msec ) /* Sleep requested number of milliseconds. */
{
#if 0
struct timeval timeout;
timeout.tv_sec = msec / 1000;
timeout.tv_usec = (msec % 1000) * 1000;
select(0, NULL, NULL, NULL, &timeout);
#else
long usecs = msec * 1000;
usleep( usecs );
#endif
}
/*---------------------------------------------------------------------------------------*/
/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */
/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */
/* a call to PaHost_FreeFastMemory(). */
void *PaHost_AllocateFastMemory(long numBytes)
{
void *addr = malloc(numBytes); /* mpin() reads into memory all pages over the given */
if (addr) /* range and locks the pages into memory. A counter */
{ /* is incremented each time the page is locked. The */
if (mpin(addr, numBytes)) /* superuser can lock as many pages as it wishes, */
{ /* others are limited to the configurable PLOCK_MA. */
ERR_RPT(("PaHost_AllocateFastMemory() failed to mpin() memory.\n"));
#if 1
free(addr); /* You MAY cut out these 2 lines to be less strict, */
addr = NULL; /* you then only get the warning but PA goes on... */
#endif /* Only problem then may be corresponding munpin() */
} /* call at PaHost_FreeFastMemory(), below. */
memset(addr, 0, numBytes); /* Locks established with mlock are not inherited by */
} /* a child process after a fork. Furthermore, IRIX- */
return addr; /* man-pages warn against mixing both mpin and mlock */
} /* in 1 piece of code, so stick to mpin()/munpin() ! */
/*---------------------------------------------------------------------------------------*/
/* Free memory that could be accessed in real-time. This call MUST be balanced with a */
/* call to PaHost_AllocateFastMemory(). */
void PaHost_FreeFastMemory(void *addr, long numBytes)
{
if (addr)
{
if (munpin(addr, numBytes)) /* Will munpin() fail when it was never mpinned? */
ERR_RPT(("WARNING: PaHost_FreeFastMemory() failed to munpin() memory.\n"));
free(addr); /* But go on, try to release it, just warn... */
}
}
/*----------------------------------------------------------*/
PaError PaHost_StreamActive( internalPortAudioStream *past )
{
PaHostSoundControl *pahsc;
if (past == NULL)
return paBadStreamPtr;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if (pahsc == NULL)
return paInternalError;
return (PaError)(past->past_IsActive != 0);
}
/*-------------------------------------------------------------------*/
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
{
internalPortAudioStream *past = (internalPortAudioStream *) stream;
/* FIXME - return actual frames played, not frames generated.
** Need to query the output device somehow.
*/
return past->past_FrameCount;
}

View file

@ -0,0 +1,52 @@
# Make PortAudio for Silicon Graphics IRIX (6.2)
# Pieter suurmond, september 22, 2001. (v15 pa_sgi sub-version #0.18)
# pthread, math (as with linux) and SGI audio library:
# SGI-books say -lpthread should be the last on the line.
LIBS = -lm -laudio -lpthread
CDEFINES = -I../pa_common
# Possible CFLAGS with MIPSpro compiler are: -32, -o32, -n32, -64,
# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging.
# And use for example -O2 or -O3 for better optimization:
CFLAGS = -O2
PASRC = ../pa_common/pa_lib.c pa_sgi.c
PAINC = ../pa_common/portaudio.h
# Tests that work (SGI Indy with R5000 @ 180MHz running IRIX 6.2).
#TESTC = $(PASRC) ../pa_tests/patest_record.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_many.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_latency.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_longsine.c # OK but needs more than 4 buffers to do without glitches.
TESTC = $(PASRC) ../pa_tests/patest_saw.c # Seems OK (does it gracefully exit?).
#TESTC = $(PASRC) ../pa_tests/patest_wire.c # OK
#TESTC = $(PASRC) ../pa_tests/pa_devs.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_sine.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_sine8.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_pink.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_clip.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_stop.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_dither.c # OK
#TESTC = $(PASRC) ../pa_tests/patest_sync.c # BEEPS and delta's, no crashes anymore.
# Tests that do not yet work.
#TESTC = $(PASRC) ../pa_tests/paqa_devs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 1 output channel(s) on AL default
#TESTC = $(PASRC) ../pa_tests/paqa_errs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 2 output channel(s) on AL default
#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c # THIS FUZZ CRASHED MY WHOLE IRIX SYSTEM after "ENTER"! :-(
# PROCESS IN ITSELF RUNS OK, WITH LARGER BUFFSIZES THOUGH.
# OH MAYBE IT WAS BECAUSE DEBUGGING WAS ON???
# ANYWAY, I'M NOT GONNA RUN THAT AGAIN... WAY TOO DANGEROUS!
TESTH = $(PAINC)
all: patest
# "cc" for the MIPSpro compiler, may be changed to "gcc":
patest: $(TESTC) $(TESTH) Makefile
cc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
run: patest
./patest

View file

@ -0,0 +1,908 @@
/*
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* SGI IRIX implementation by Pieter Suurmond, september 22, 2001 (#0.18).
*
* Copyright (c) 1999-2001 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.
*/
/*
Modfication History:
8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2.
8/17/2001 - alpha release with IRIX sproc()-method, may sometimes let IRIX6.2 crash at closing audiostream.
9/22/2001 - #0.18 pthread starts to work a bit:
BUT UNDER IRIX6.2, I DON'T GET IT TO WORK REALLY CORRECTLY,
this POSIX-attempt,
DON'T USE THIS FILE FOR RELIABLE OPERATION, IT IS HERE JUST
FOR DOCUMENTATION/ARCHIVE... OR FOR ANYONE WHO WANTS TO FIX......
TODO:
- Test under IRIX 6.5.
- Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion),
and maybe also the other natively supported formats? (might increase performance)
- Not sure whether CPU UTILIZATION MEASUREMENT (from OSS/linux) really works. Changed nothing yet,
seems ok, but I've not yet tested it thoroughly. (maybe utilization-code may be made _unix_common_ then?)
- The minimal number of buffers setting... I do not yet fully understand it.. I now just take *4.
REFERENCES:
- IRIX 6.2 man pages regarding SGI AL library.
- IRIS Digital MediaProgramming Guide (online books as well as man-pages come with IRIX 6.2 and
may not be publically available on the internet).
*/
#include <stdio.h> /* Standard libraries. */
#include <stdlib.h>
#include "../pa_common/portaudio.h" /* Portaudio headers. */
#include "../pa_common/pa_host.h"
#include "../pa_common/pa_trace.h"
/*
#include <malloc.h>
#include <memory.h>
#include <sys/prctl.h> Not needed
#include <sys/types.h>
#include <sys/schedctl.h>
#include <signal.h>
#include <sys/ioctl.h> Needed?
#include <sys/time.h>
#include <sched.h> sched_param struct and related functions
used in setting thread priorities.
#include <limits.h> Some POSIX constants such as _POSIX_THREAD_THREADS_MAX
*/
#include <pthread.h> /* Pthreads are supported by IRIX 6.2 after */
/* patches 1361, 1367, and 1429 are applied. */
#include <fcntl.h> /* fcntl.h needed for "O_RDONLY". */
#include <unistd.h> /* For usleep() and constants used when calling sysconf() */
/* to query POSIX limits (see the sysconf(3) ref. page. */
#include <dmedia/audio.h> /* SGI-specific audio library. */
/*--------------------------------------------*/
#define PRINT(x) { printf x; fflush(stdout); }
#define ERR_RPT(x) PRINT(x)
#define DBUG(x) PRINT(x)
#define DBUGX(x) /* PRINT(x) */
#define MAX_CHARS_DEVNAME (16) /* Was 32 in OSS (20 for AL but "in"/"out" is concat. */
#define MAX_SAMPLE_RATES (8) /* Known from SGI AL there are 7 (was 10 in OSS v15). */
typedef struct internalPortAudioDevice /* IRIX specific device info: */
{
PaDeviceID /* NEW: */ pad_DeviceID; /* THIS "ID" IS NEW HERE (Pieter)! */
long pad_ALdevice; /* SGI-number! */
double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */
char pad_DeviceName[MAX_CHARS_DEVNAME+1]; /* +1 for \0, one more than OSS. */
PaDeviceInfo pad_Info; /* pad_Info (v15) contains: */
/* int structVersion; */
/* const char* name; */
/* int maxInputChannels, maxOutputChannels; */
/* int numSampleRates; Num rates, or -1 if range supprtd. */
/* const double* sampleRates; Array of supported sample rates, */
/* PaSampleFormat nativeSampleFormats; or {min,max} if range supported. */
struct internalPortAudioDevice* pad_Next; /* Singly linked list, (NULL=end). */
} internalPortAudioDevice;
typedef struct PaHostSoundControl /* Structure to contain all SGI IRIX specific data. */
{
ALport pahsc_ALportIN, /* IRIX-audio-library-datatype. ALports can only be */
pahsc_ALportOUT; /* unidirectional, so we sometimes need 2 of them. */
pthread_t pahsc_ThreadPID;
short *pahsc_NativeInputBuffer, /* Allocated here, in this file, if necessary. */
*pahsc_NativeOutputBuffer;
unsigned int pahsc_BytesPerInputBuffer, /* Native buffer sizes in bytes, really needed here */
pahsc_BytesPerOutputBuffer; /* to free FAST memory, if buffs were alloctd FAST. */
unsigned int pahsc_SamplesPerInputBuffer, /* These amounts are needed again and again in the */
pahsc_SamplesPerOutputBuffer; /* audio-thread (don't need to be kept globally). */
struct itimerval pahsc_EntryTime, /* For measuring CPU utilization (same as linux). */
pahsc_LastExitTime;
long pahsc_InsideCountSum,
pahsc_TotalCountSum;
} PaHostSoundControl;
/*----------------------------- Shared Data ------------------------------------------------------*/
static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */
static int sPaHostError = 0; /* Maybe more than one process writing errs!? */
long Pa_GetHostError(void)
{
return (long)sPaHostError;
}
/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/
/* (copied from source pa_linux_oss/pa_linux_oss.c) */
static void Pa_StartUsageCalculation( internalPortAudioStream *past )
{
struct itimerval itimer;
PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData;
if( pahsc == NULL ) return;
/* Query system timer for usage analysis and to prevent overuse of CPU. */
getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime );
}
static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB )
{
long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec;
long usecs = secs * 1000000;
usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec);
return usecs;
}
static void Pa_EndUsageCalculation( internalPortAudioStream *past )
{
struct itimerval currentTime;
long insideCount;
long totalCount; /* Measure CPU utilization during this callback. */
#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 (getitimer( ITIMER_REAL, &currentTime ) == 0 )
{
if (past->past_IfLastExitValid)
{
insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, &currentTime );
pahsc->pahsc_InsideCountSum += insideCount;
totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, &currentTime );
pahsc->pahsc_TotalCountSum += totalCount;
/* DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */
/* Low pass filter the result because sometimes we get called several times in a row. */
/* That can cause the TotalCount to be very low which can cause the usage to appear */
/* unnaturally high. So we must filter numerator and denominator separately!!! */
if (pahsc->pahsc_InsideCountSum > 0)
{
past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) +
(LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum));
past->past_AverageTotalCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) +
(LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum));
past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount;
pahsc->pahsc_InsideCountSum = 0;
pahsc->pahsc_TotalCountSum = 0;
}
}
past->past_IfLastExitValid = 1;
}
pahsc->pahsc_LastExitTime.it_value.tv_sec = 100;
pahsc->pahsc_LastExitTime.it_value.tv_usec = 0;
setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL );
past->past_IfLastExitValid = 1;
} /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/
/*--------------------------------------------------------------------------------------*/
PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */
{ /* call to report via ERR_RPT(), yields a PaError-num. */
const char* a = "SGI AL "; /* (Not absolutely sure errno came from THIS thread! */
switch(oserror()) /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */
{
case AL_BAD_OUT_OF_MEM:
ERR_RPT(("%sout of memory.\n", a));
return paInsufficientMemory; /* Known PaError. */
case AL_BAD_CONFIG:
ERR_RPT(("%sconfiguration invalid or NULL.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_CHANNELS:
ERR_RPT(("%schannels not 1,2 or 4.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_NO_PORTS:
ERR_RPT(("%sout of audio ports.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_DEVICE:
ERR_RPT(("%swrong device number.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_DEVICE_ACCESS:
ERR_RPT(("%swrong device access.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_DIRECTION:
ERR_RPT(("%sinvalid direction.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_SAMPFMT:
ERR_RPT(("%sdoesn't accept sampleformat.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_FLOATMAX:
ERR_RPT(("%smax float value is zero.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_WIDTH:
ERR_RPT(("%sunsupported samplewidth.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_QSIZE:
ERR_RPT(("%sinvalid queue size.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_PVBUFFER:
ERR_RPT(("%sPVbuffer null.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_BUFFERLENGTH_NEG:
ERR_RPT(("%snegative bufferlength.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_BUFFERLENGTH_ODD:
ERR_RPT(("%sodd bufferlength.\n", a));
return paHostError; /* Generic PaError. */
case AL_BAD_PARAM:
ERR_RPT(("%sparameter not valid for device.\n", a));
return paHostError; /* Generic PaError. */
default:
ERR_RPT(("%sunknown error.\n", a));
return paHostError; /* Generic PaError. */
}
}
/*------------------------------------------------------------------------------------------*/
/* Tries to set various rates and formats and fill in the device info structure. */
static PaError Pa_sgiQueryDevice(long ALdev, /* (AL_DEFAULT_DEVICE) */
PaDeviceID id, /* (DefaultI|ODeviceID()) */
char* name, /* (for example "SGI AL") */
internalPortAudioDevice* pad) /* Result written to pad. */
{
int format;
long min, max; /* To catch hardware characteristics. */
ALseterrorhandler(0); /* 0 = turn off the default error handler. */
/*--------------------------------------------------------------------------------------*/
pad->pad_ALdevice = ALdev; /* Set the AL device number. */
pad->pad_DeviceID = id; /* Set the PA device number. */
if (strlen(name) > MAX_CHARS_DEVNAME) /* MAX_CHARS defined above. */
{
ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name));
return paHostError;
}
strcpy(pad->pad_DeviceName, name); /* Write name-string. */
pad->pad_Info.name = pad->pad_DeviceName; /* Set pointer,..hmmm. */
/*--------------------------------- natively supported sample formats: -----------------*/
pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */
/* Then also choose other CallConvertXX()! */
/*--------------------------------- number of available i/o channels: ------------------*/
if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max))
return translateSGIerror();
pad->pad_Info.maxInputChannels = max;
DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels))
if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max))
return translateSGIerror();
pad->pad_Info.maxOutputChannels = max;
DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels))
/*--------------------------------- supported samplerates: ----------------------*/
pad->pad_Info.numSampleRates = 7;
pad->pad_Info.sampleRates = pad->pad_SampleRates;
pad->pad_SampleRates[0] = (double)AL_RATE_8000; /* long -> double. */
pad->pad_SampleRates[1] = (double)AL_RATE_11025;
pad->pad_SampleRates[2] = (double)AL_RATE_16000;
pad->pad_SampleRates[3] = (double)AL_RATE_22050;
pad->pad_SampleRates[4] = (double)AL_RATE_32000;
pad->pad_SampleRates[5] = (double)AL_RATE_44100;
pad->pad_SampleRates[6] = (double)AL_RATE_48000;
if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max)) /* Ask INPUT rate-max. */
return translateSGIerror(); /* double -> long. */
if (max != (long)(0.5 + pad->pad_SampleRates[6])) /* FP-compare not recommndd. */
goto weird;
if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max. */
return translateSGIerror();
if (max != (long)(0.5 + pad->pad_SampleRates[6]))
{
weird: ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max));
return paHostError; /* Or make it a warning and just carry on... */
}
/*-------------------------------------------------------------------------------*/
return paNoError;
}
/*--------------------------------------------------------------------------------*/
int Pa_CountDevices() /* Name of this function suggests it only counts and */
{ /* is NOT destructive, it however resets whole PA ! */
int numDevices = 0; /* Let 's not do that here. */
internalPortAudioDevice* currentDevice = sDeviceList; /* COPY GLOBAL VAR. */
#if 0 /* Remains from linux_oss v15: Pa_Initialize(), on */
if (!currentDevice) /* its turn, calls PaHost_Init() via file pa_lib.c. */
Pa_Initialize(); /* Isn't that a bit too 'rude'? Don't be too */
#endif /* friendly to clients that forgot to initialize PA. */
while (currentDevice) /* Slower but more elegant than the sNumDevices-way: */
{
numDevices++;
currentDevice = currentDevice->pad_Next;
}
return numDevices;
}
/*-------------------------------------------------------------------------------*/
static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id)
{
int numDevices = 0;
internalPortAudioDevice *res = (internalPortAudioDevice*)NULL;
internalPortAudioDevice *pad = sDeviceList; /* COPY GLOBAL VAR. */
while (pad) /* pad may be NULL, that's ok, return 0. */
{ /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.) */
if (pad->pad_DeviceID == id) /* This the device we were looking for? */
res = pad; /* But keep on(!) counting so we don't */
numDevices++; /* have to call Pa_CountDevices() later. */
pad = pad->pad_Next; /* Advance to the next device or NULL. */
} /* No assumptions about order of ID's in */
if (!res) /* the list. */
ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id));
if ((id < 0) || (id >= numDevices))
{
ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id));
#if 1 /* Be strict, even when found, */
res = (internalPortAudioDevice*)NULL; /* do not accept illegal ID's. */
#endif
}
return res;
}
/*----------------------------------------------------------------------*/
const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id)
{
PaDeviceInfo* res = (PaDeviceInfo*)NULL;
internalPortAudioDevice* pad = Pa_GetInternalDevice(id); /* Call. */
if (pad)
res = &pad->pad_Info; /* Not finding the specified ID is not */
if (!res) /* the same as &pad->pad_Info == NULL. */
ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id));
return res; /* So (maybe) a second/third ERR_RPT(). */
}
/*------------------------------------------------*/
PaDeviceID Pa_GetDefaultInputDeviceID(void)
{
return 0; /* 0 is the default device ID. */
}
/*------------------------------------------------*/
PaDeviceID Pa_GetDefaultOutputDeviceID(void)
{
return 0;
}
/*-------------------------------------------------------------------------------------------------*/
/* Build linked a list with all the available audio devices on this SGI machine (only 1 for now). */
PaError PaHost_Init(void) /* Called by Pa_Initialize() from pa_lib.c. */
{
internalPortAudioDevice* pad;
PaError r = paNoError;
int audioLibFileID; /* To test for the presence of audio. */
if (sDeviceList) /* Allow re-init, only warn, no error. */
{
ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n"));
return r;
}
/*------------- ADD THE SGI DEFAULT DEVICE TO THE LIST: ---------------------------------------*/
audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY); /* Try to open Indigo style audio */
if (audioLibFileID < 0) /* IO port. On failure, machine */
{ /* has no audio ability. */
ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n"));
return paHostError;
}
close(audioLibFileID); /* Allocate fast mem to hold device info. */
pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice));
if (pad == NULL)
return paInsufficientMemory;
memset(pad, 0, sizeof(internalPortAudioDevice)); /* "pad->pad_Next = NULL" is more elegant. */
r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE, /* Set AL device num (AL_DEFAULT_DEVICE). */
Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */
"AL default", /* A suitable name. */
pad); /* Write args and queried info into pad. */
if (r != paNoError)
{
ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r));
PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); /* sDeviceList still NULL ! */
}
else
sDeviceList = pad; /* First element in linked list. pad->pad_Next already NULL. */
/*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LINKED LIST: -------------------*/
/*---------------------------------------------------------------------------------------------*/
return r;
}
/*---------------------------------------------------------------------------------------------------*/
static PaError Pa_SgiAudioProcess(internalPortAudioStream *past) /* Spawned by PaHost_StartEngine(). */
{
PaError result = paNoError;
PaHostSoundControl *pahsc;
if (!past)
return paBadStreamPtr;
pahsc = (PaHostSoundControl*)past->past_DeviceData;
if (!pahsc)
return paInternalError;
past->past_IsActive = 1; /* Wasn't this already done by the calling parent?! */
DBUG(("entering thread.\n"));
while (!past->past_StopSoon) /* OR-ing StopSoon and StopNow here gives problems! */
{
/*---------------------------------------- INPUT: ------------------------------------*/
if (pahsc->pahsc_NativeInputBuffer) /* Then pahsc_ALportIN should also be there! */
{
while (ALgetfilled(pahsc->pahsc_ALportIN) < pahsc->pahsc_SamplesPerInputBuffer)
{
/* Trying sginap(1); and usleep(); here... things get blocked under IRIX6.2. */
if (past->past_StopNow) /* Don't let ALreadsamps() block */
goto done;
}
if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_NativeInputBuffer,
pahsc->pahsc_SamplesPerInputBuffer)) /* Number of samples instead */
{ /* of number of frames. */
ERR_RPT(("ALreadsamps() failed.\n"));
result = paInternalError;
goto done;
}
}
/*---------------------------------------------------- USER CALLBACK ROUTINE: ----------*/
/* DBUG(("Calling Pa_CallConvertInt16()...\n")); */
Pa_StartUsageCalculation(past); /* Convert 16 bit native data to */
result = Pa_CallConvertInt16(past, /* user data and call user routine. */
pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_NativeOutputBuffer);
Pa_EndUsageCalculation(past);
if (result)
{
DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result));
goto done; /* This is apparently NOT an error! */
} /* Just letting the userCallBack stop us. */
/*---------------------------------------- OUTPUT: ------------------------------------*/
if (pahsc->pahsc_NativeOutputBuffer) /* Then pahsc_ALportOUT should also be there! */
{
while (ALgetfillable(pahsc->pahsc_ALportOUT) < pahsc->pahsc_SamplesPerOutputBuffer)
{
/* Trying sginap(1); and usleep(); here... things get blocked under IRIX6.2. */
if (past->past_StopNow) /* Don't let ALwritesamps() block */
goto done;
}
if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_NativeOutputBuffer,
pahsc->pahsc_SamplesPerOutputBuffer))
{
ERR_RPT(("ALwritesamps() failed.\n"));
result = paInternalError;
goto done;
}
}
/*-------------------------------------------------------------------------------------*/
}
done:
/* pahsc->pahsc_ThreadPID = -1; Hu? doesn't help!! (added by Pieter) */
past->past_IsActive = 0;
DBUG(("leaving thread.\n"));
return result;
}
/*--------------------------------------------------------------------------------------*/
PaError PaHost_OpenStream(internalPortAudioStream *past)
{
PaError result = paNoError;
PaHostSoundControl *pahsc;
unsigned int minNumBuffers;
internalPortAudioDevice *padIN, *padOUT; /* For looking up native AL-numbers. */
ALconfig sgiALconfig = NULL; /* IRIX-datatype. */
long pvbuf[8]; /* To get/set hardware configs. */
long sr, ALqsize;
DBUG(("PaHost_OpenStream() called.\n")); /* Alloc FASTMEM and init host data. */
if (!past)
{
ERR_RPT(("Streampointer NULL!\n"));
result = paBadStreamPtr; goto done;
}
pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl));
if (pahsc == NULL)
{
ERR_RPT(("FAST Memory allocation failed.\n")); /* Pass trough some ERR_RPT-exit- */
result = paInsufficientMemory; goto done; /* code (nothing will be freed). */
}
memset(pahsc, 0, sizeof(PaHostSoundControl));
/* pahsc->pahsc_threadPID = -1; Should pahsc_threadPID be inited to */
past->past_DeviceData = (void*)pahsc; /* -1 instead of 0 ?? */
/*--------------------------------------------------- Allocate native buffers: --------*/
pahsc->pahsc_SamplesPerInputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
past->past_NumInputChannels; /* audio-thread. */
pahsc->pahsc_BytesPerInputBuffer = pahsc->pahsc_SamplesPerInputBuffer * sizeof(short);
if (past->past_NumInputChannels > 0) /* Assumes short = 16 bits! */
{
pahsc->pahsc_NativeInputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputBuffer);
if( pahsc->pahsc_NativeInputBuffer == NULL )
{
ERR_RPT(("Fast memory allocation for input-buffer failed.\n"));
result = paInsufficientMemory; goto done;
}
}
pahsc->pahsc_SamplesPerOutputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */
past->past_NumOutputChannels; /* audio-thread. */
pahsc->pahsc_BytesPerOutputBuffer = pahsc->pahsc_SamplesPerOutputBuffer * sizeof(short);
if (past->past_NumOutputChannels > 0) /* Assumes short = 16 bits! */
{
pahsc->pahsc_NativeOutputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputBuffer);
if (pahsc->pahsc_NativeOutputBuffer == NULL)
{
ERR_RPT(("Fast memory allocation for output-buffer failed.\n"));
result = paInsufficientMemory; goto done;
}
}
/*------------------------------------------ Manipulate hardware if necessary and allowed: --*/
ALseterrorhandler(0); /* 0 = turn off the default error handler. */
pvbuf[0] = AL_INPUT_RATE;
pvbuf[2] = AL_INPUT_COUNT;
pvbuf[4] = AL_OUTPUT_RATE; /* TO FIX: rates may be logically, not always in Hz! */
pvbuf[6] = AL_OUTPUT_COUNT;
sr = (long)(past->past_SampleRate + 0.5); /* Common for input and output :-) */
if (past->past_NumInputChannels > 0) /* We need to lookup the corre- */
{ /* sponding native AL-number(s). */
padIN = Pa_GetInternalDevice(past->past_InputDeviceID);
if (!padIN)
{
ERR_RPT(("Pa_GetInternalDevice() for input failed.\n"));
result = paHostError; goto done;
}
if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */
goto sgiError; /* the same AL-device, the AL-library might */
if (pvbuf[1] != sr) /* contain more than AL_DEFAULT_DEVICE in */
{ /* Rate different from current harware-rate? the future. Therefore 2 seperate queries. */
if (pvbuf[3] > 0) /* Means, there's other clients using AL-input-ports */
{
ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \
another process is currently using input at %ld kHz.\n", sr, pvbuf[1]));
result = paHostError; goto done;
}
pvbuf[1] = sr; /* Then set input-rate. */
if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2))
goto sgiError; /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */
} /* IS NOT CHECKED, AT LEAST NOT BY ME, WITHIN THIS FILE! Does PA do? */
}
if (past->past_NumOutputChannels > 0) /* CARE: padOUT/IN may NOT be NULL if Channels <= 0! */
{ /* We use padOUT/IN later on, or at least 1 of both. */
padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID);
if (!padOUT)
{
ERR_RPT(("Pa_GetInternalDevice() for output failed.\n"));
result = paHostError; goto done;
}
if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4))
goto sgiError;
if ((past->past_NumOutputChannels > 0) && (pvbuf[5] != sr))
{ /* Output needed and rate different from current harware-rate. */
if (pvbuf[7] > 0) /* Means, there's other clients using AL-output-ports */
{
ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \
another process is currently using output at %ld kHz.\n", sr, pvbuf[5]));
result = paHostError; goto done; /* Will free again the inputbuffer */
} /* that was just created above. */
pvbuf[5] = sr; /* Then set output-rate. */
if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2))
goto sgiError;
}
}
/*------------------------------------------ Construct an audio-port-configuration ----------*/
sgiALconfig = ALnewconfig(); /* Change the SGI-AL-default-settings. */
if (sgiALconfig == (ALconfig)0) /* sgiALconfig is released here after use! */
goto sgiError; /* See that sgiALconfig is NOT released! */
if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_TWOSCOMP)) /* Choose paInt16 as native i/o-format. */
goto sgiError;
if (ALsetwidth (sgiALconfig, AL_SAMPLE_16)) /* Only meaningful when sample format for */
goto sgiError; /* config is set to two's complement format. */
/************************ Future versions might (dynamically) switch to 32-bit floats? *******
if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_FLOAT)) (Then also call another CallConvert-func.)
goto sgiError;
if (ALsetfloatmax (sgiALconfig, 1.0)) Only meaningful when sample format for config
goto sgiError; is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */
/*---------- ?? --------------------*/
/* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */
minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer, past->past_SampleRate);
past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ?
minNumBuffers : past->past_NumUserBuffers;
/*------------------------------------------------ Set internal AL queuesize (in samples) ----*/
if (pahsc->pahsc_SamplesPerInputBuffer >= pahsc->pahsc_SamplesPerOutputBuffer)
ALqsize = (long)pahsc->pahsc_SamplesPerInputBuffer;
else /* Take the largest of the two amounts. */
ALqsize = (long)pahsc->pahsc_SamplesPerOutputBuffer;
ALqsize *= 4; /* 4 times as large as amount per transfer! */
if (ALsetqueuesize(sgiALconfig, ALqsize)) /* Or should we use past_NumUserBuffers here? */
goto sgiError; /* Using 2 times may give glitches... */
/* Have to work on ALsetqueuesize() above. */
/* Do ALsetchannels() later, apart per input and/or output. */
/*----------------------------------------------- OPEN 1 OR 2 AL-DEVICES: --------------------*/
if (past->past_OutputDeviceID == past->past_InputDeviceID) /* Who SETS these devive-numbers? */
{
if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0))
{
DBUG(("PaHost_OpenStream: opening both input and output channels.\n"));
/*------------------------- open output port: ----------------------------------*/
if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
goto sgiError; /* Returns 0 on success, -1 on failure. */
pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
if (pahsc->pahsc_ALportOUT == (ALport)0)
goto sgiError;
/*------------------------- open input port: -----------------------------------*/
if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
goto sgiError; /* Returns 0 on success, -1 on failure. */
pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
if (pahsc->pahsc_ALportIN == (ALport)0)
goto sgiError; /* For some reason the "patest_wire.c"-test crashes! */
} /* Probably due to too small buffersizes?.... */
else
{
ERR_RPT(("Cannot setup bidirectional stream between different devices.\n"));
result = paHostError;
goto done;
}
}
else /* (OutputDeviceID != InputDeviceID) */
{
if (past->past_NumOutputChannels > 0) /* WRITE-ONLY: */
{
/*------------------------- open output port: ----------------------------------*/
DBUG(("PaHost_OpenStream: opening %d output channel(s) on %s.\n",
past->past_NumOutputChannels, padOUT->pad_DeviceName));
if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels)))
goto sgiError; /* Returns 0 on success, -1 on failure. */
pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig);
if (pahsc->pahsc_ALportOUT == (ALport)0)
goto sgiError;
}
if (past->past_NumInputChannels > 0) /* READ-ONLY: */
{
/*------------------------- open input port: -----------------------------------*/
DBUG(("PaHost_OpenStream: opening %d input channel(s) on %s.\n",
past->past_NumInputChannels, padIN->pad_DeviceName));
if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels)))
goto sgiError; /* Returns 0 on success, -1 on failure. */
pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig);
if (pahsc->pahsc_ALportIN == (ALport)0)
goto sgiError;
}
}
DBUG(("PaHost_OpenStream() succeeded.\n"));
goto done; /* (no errors occured) */
sgiError:
result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
done:
if (sgiALconfig)
ALfreeconfig(sgiALconfig); /* We don't need that struct anymore. */
if (result != paNoError)
PaHost_CloseStream(past); /* Frees memory (only if really allocated!). */
return result;
}
/*-----------------------------------------------------*/
PaError PaHost_StartOutput(internalPortAudioStream *past)
{
return paNoError; /* Hmm, not implemented yet? */
}
PaError PaHost_StartInput(internalPortAudioStream *past)
{
return paNoError;
}
/*------------------------------------------------------------------------------*/
PaError PaHost_StartEngine(internalPortAudioStream *past)
{
PaHostSoundControl *pahsc;
int hres;
PaError result = paNoError;
if (!past) /* Test argument. */
{
ERR_RPT(("PaHost_StartEngine(NULL)!\n"));
return paBadStreamPtr;
}
pahsc = (PaHostSoundControl*)past->past_DeviceData;
if (!pahsc)
{
ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData == NULL!\n"));
return paHostError;
}
past->past_StopSoon = 0; /* Assume SGI ALport is already opened! */
past->past_StopNow = 0;
past->past_IsActive = 1;
DBUG(("PaHost_StartEngine() called.\n"));
/* Use pthread_create() instead of __clone() because: */
/* - pthread_create also works for other UNIX systems like Solaris, */
/* - Java HotSpot VM crashes in pthread_setcanceltype() when using __clone(). */
hres = pthread_create(&(pahsc->pahsc_ThreadPID), /* SPAWN AUDIO-CHILD. */
NULL, /* pthread_attr_t * attr */
(void*)Pa_SgiAudioProcess,
past);
if (hres)
{
result = paHostError;
sPaHostError = hres;
ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n"));
}
return result;
}
/*------------------------------------------------------------------------------*/
PaError PaHost_StopEngine(internalPortAudioStream *past, int abort)
{
int hres;
PaError result = paNoError;
PaHostSoundControl *pahsc;
DBUG(("PaHost_StopEngine() called.\n"));
if (!past)
return paBadStreamPtr;
pahsc = (PaHostSoundControl*)past->past_DeviceData;
if (pahsc == NULL)
return result; /* paNoError (already stopped, no err?). */
past->past_StopSoon = 1; /* Tell background thread to stop generating */
if (abort) /* more and to let current data play out. If */
past->past_StopNow = 1; /* aborting, tell backgrnd thread to stop NOW! */
if (pahsc->pahsc_ThreadPID != -1) /* Join thread to recover memory resources. */
{
DBUG(("pthread_join() called.\n"));
hres = pthread_join(pahsc->pahsc_ThreadPID, NULL);
if (hres)
{
result = paHostError;
sPaHostError = hres;
ERR_RPT(("PaHost_StopEngine() failed pthread_join().\n"));
}
pahsc->pahsc_ThreadPID = -1;
}
past->past_IsActive = 0;
return result;
}
/*---------------------------------------------------------------*/
PaError PaHost_StopOutput(internalPortAudioStream *past, int abort)
{
return paNoError; /* Not implemented yet? */
}
PaError PaHost_StopInput(internalPortAudioStream *past, int abort )
{
return paNoError;
}
/*******************************************************************/
PaError PaHost_CloseStream(internalPortAudioStream *past)
{
PaHostSoundControl *pahsc;
PaError result = paNoError;
DBUG(("PaHost_CloseStream() called.\n"));
if (past == NULL)
return paBadStreamPtr;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if (pahsc == NULL) /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */
return result; /* This test prevents from freeing NULL-pointers. */
if (pahsc->pahsc_ALportIN)
{
if (ALcloseport(pahsc->pahsc_ALportIN))
result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */
else /* But go on anyway... to release other stuff... */
pahsc->pahsc_ALportIN = (ALport)0;
}
if (pahsc->pahsc_ALportOUT)
{
if (ALcloseport(pahsc->pahsc_ALportOUT))
result = translateSGIerror();
else
pahsc->pahsc_ALportOUT = (ALport)0;
}
if (pahsc->pahsc_NativeInputBuffer)
{
PaHost_FreeFastMemory(pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_BytesPerInputBuffer);
pahsc->pahsc_NativeInputBuffer = NULL;
}
if (pahsc->pahsc_NativeOutputBuffer)
{
PaHost_FreeFastMemory(pahsc->pahsc_NativeOutputBuffer, pahsc->pahsc_BytesPerOutputBuffer);
pahsc->pahsc_NativeOutputBuffer = NULL;
}
PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl));
past->past_DeviceData = NULL; /* PaHost_OpenStream() allocated FAST. */
return result;
}
/*************************************************************************
** Determine minimum number of buffers required for this host based
** on minimum latency. Latency can be optionally set by user by setting
** an environment variable. For example, to set latency to 200 msec, put:
** set PA_MIN_LATENCY_MSEC=200
** in the AUTOEXEC.BAT file and reboot.
** If the environment variable is not set, then the latency will be
** determined based on the OS. Windows NT has higher latency than Win95.
*/
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate )
{
return 2;
}
/* Hmmm, the note above isn't appropriate for SGI I'm afraid... */
/* Do we HAVE to do it this way under IRIX???.... */
/*--------------------------------------------------------------*/
/*---------------------------------------------------------------------*/
PaError PaHost_Term(void) /* Frees all of the linked audio-devices. */
{ /* Called by Pa_Terminate() from pa_lib.c. */
internalPortAudioDevice *pad = sDeviceList,
*nxt;
while (pad)
{
DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName));
nxt = pad->pad_Next;
PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice));
pad = nxt; /* PaHost_Init allocated this FAST MEM.*/
}
sDeviceList = (internalPortAudioDevice*)NULL;
return 0; /* Got rid of sNumDevices=0; */
}
/***********************************************************************/
void Pa_Sleep( long msec ) /* Sleep requested number of milliseconds. */
{
#if 0
struct timeval timeout;
timeout.tv_sec = msec / 1000;
timeout.tv_usec = (msec % 1000) * 1000;
select(0, NULL, NULL, NULL, &timeout);
#else
long usecs = msec * 1000;
usleep( usecs );
#endif
}
/*---------------------------------------------------------------------------------------*/
/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */
/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */
/* a call to PaHost_FreeFastMemory(). */
void *PaHost_AllocateFastMemory(long numBytes)
{
void *addr = malloc(numBytes);
if (addr)
memset(addr, 0, numBytes);
return addr;
}
/*---------------------------------------------------------------------------------------*/
/* Free memory that could be accessed in real-time. This call MUST be balanced with a */
/* call to PaHost_AllocateFastMemory(). */
void PaHost_FreeFastMemory(void *addr, long numBytes)
{
if (addr)
free(addr);
}
/*----------------------------------------------------------*/
PaError PaHost_StreamActive (internalPortAudioStream *past)
{
PaHostSoundControl *pahsc;
if (past == NULL)
return paBadStreamPtr;
pahsc = (PaHostSoundControl *) past->past_DeviceData;
if (pahsc == NULL)
return paInternalError;
return (PaError)(past->past_IsActive != 0);
}
/*-------------------------------------------------------------------*/
PaTimestamp Pa_StreamTime( PortAudioStream *stream )
{
internalPortAudioStream *past = (internalPortAudioStream *) stream;
/* FIXME - return actual frames played, not frames generated.
** Need to query the output device somehow.
*/
return past->past_FrameCount;
}

View file

@ -0,0 +1,131 @@
/*
* $Id$
* Convert tagged values.
*
* Author: Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
//#define OUTPUT_DEVICE (11)
#define NUM_SECONDS (8)
#define SLEEP_DUR (800)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#define NUM_BUFFERS (0)
typedef struct
{
unsigned int framesToGo;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
short *out = (short*)outputBuffer;
int i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
if( data->framesToGo < framesPerBuffer ) finished = 1;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = 0x0000 + i; /* left */
*out++ = 0x1000 + i; /* right */
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int totalSamps;
printf("PortAudio Test: output debug values\n" );
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
printf("totalSamps = %d\n", totalSamps );
err = Pa_Initialize();
if( err != paNoError ) goto error;
printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
2, /* stereo output */
paInt16, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Is callback being called?\n");
for( i=0; i<((NUM_SECONDS+1)*1000); i+=SLEEP_DUR )
{
printf("data.framesToGo = %d\n", data.framesToGo ); fflush(stdout);
Pa_Sleep( SLEEP_DUR );
}
/* Stop sound until ENTER hit. */
printf("Call Pa_StopStream()\n");
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,55 @@
/*
* $Id$
* Test Dither calculations.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#include "pa_host.h"
/*******************************************************************/
int main(void);
int main(void)
{
long max,min;
int i;
for( i=0; i<10000; i++ )
{
long dither = PaConvert_TriangularDither();
// printf("dither = 0x%08X\n", dither );
if( dither < min ) min = dither;
else if( dither > max ) max = dither;
}
printf("min = 0x%08X = %d, max = 0x%08X = %d\n", min, min, max, max );
}

View file

@ -0,0 +1,183 @@
/*
* $Id$
* debug_dual.c
* Try to open TWO streams on separate cards.
* Play a sine sweep using the Portable Audio api for several seconds.
* Hacked test for debugging PA.
*
* Author: Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define DEV_ID_1 (13)
#define DEV_ID_2 (15)
#define NUM_SECONDS (8)
#define SLEEP_DUR (800)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#if 0
#define MIN_LATENCY_MSEC (200)
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
#else
#define NUM_BUFFERS (0)
#endif
#define MIN_FREQ (100.0f)
#define MAX_FREQ (4000.0f)
#define FREQ_SCALAR (1.00002f)
#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (400)
typedef struct
{
float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
float phase_increment;
float left_phase;
float right_phase;
}
paTestData;
/* Convert phase between and 1.0 to sine value
* using linear interpolation.
*/
float LookupSine( paTestData *data, float phase );
float LookupSine( paTestData *data, float phase )
{
float fIndex = phase*TABLE_SIZE;
int index = (int) fIndex;
float fract = fIndex - index;
float lo = data->sine[index];
float hi = data->sine[index+1];
float val = lo + fract*(hi-lo);
return val;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = LookupSine(data, data->left_phase); /* left */
*out++ = LookupSine(data, data->right_phase); /* right */
data->left_phase += data->phase_increment;
if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
/* sweep frequency then start over. */
data->phase_increment *= FREQ_SCALAR;
if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
}
return 0;
}
PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID,
paTestData *data );
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream1, *stream2;
PaError err;
paTestData DATA1, DATA2;
printf("PortAudio Test: DUAL sine sweep. ask for %d buffers\n", NUM_BUFFERS );
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = TestStart( &stream1, DEV_ID_1, &DATA1 );
if( err != paNoError ) goto error;
err = TestStart( &stream2, DEV_ID_2, &DATA2 );
if( err != paNoError ) goto error;
printf("Hit ENTER\n");
getchar();
err = Pa_StopStream( stream1 );
if( err != paNoError ) goto error;
err = Pa_StopStream( stream2 );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}
PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, paTestData *data )
{
PortAudioStream *stream;
PaError err;
int i;
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data->sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data->sine[TABLE_SIZE] = data->sine[0]; // set guard point
data->left_phase = data->right_phase = 0.0;
data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
printf("PortAudio Test: output device = %d\n", devID );
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
devID,
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
*streamPtr = stream;
return 0;
error:
return err;
}

View file

@ -0,0 +1,179 @@
/*
* $Id$
* debug_multi_in.c
* Pass output from each of multiple channels
* to a stereo output using the Portable Audio api.
* Hacked test for debugging PA.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include <string.h>
#include "portaudio.h"
//#define INPUT_DEVICE_NAME ("EWS88 MT Interleaved Rec")
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
//#define OUTPUT_DEVICE (18)
#define SAMPLE_RATE (22050)
#define FRAMES_PER_BUFFER (256)
#define MIN_LATENCY_MSEC (400)
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
#ifndef M_PI
#define M_PI (3.14159265)
#endif
typedef struct
{
int liveChannel;
int numChannels;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
float *in = (float*)inputBuffer;
int i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
if( in == NULL ) return 0;
for( i=0; i<(int)framesPerBuffer; i++ )
{
/* Copy one channel of input to output. */
*out++ = in[data->liveChannel];
*out++ = in[data->liveChannel];
in += data->numChannels;
}
return 0;
}
/*******************************************************************/
int PaFindDeviceByName( const char *name )
{
int i;
int numDevices;
const PaDeviceInfo *pdi;
int len = strlen( name );
PaDeviceID result = paNoDevice;
numDevices = Pa_CountDevices();
for( i=0; i<numDevices; i++ )
{
pdi = Pa_GetDeviceInfo( i );
if( strncmp( name, pdi->name, len ) == 0 )
{
result = i;
break;
}
}
return result;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
PaDeviceID inputDevice;
const PaDeviceInfo *pdi;
printf("PortAudio Test: input signal from each channel. %d buffers\n", NUM_BUFFERS );
data.liveChannel = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
#ifdef INPUT_DEVICE_NAME
printf("Try to use device: %s\n", INPUT_DEVICE_NAME );
inputDevice = PaFindDeviceByName(INPUT_DEVICE_NAME);
if( inputDevice == paNoDevice )
{
printf("Could not find %s. Using default instead.\n", INPUT_DEVICE_NAME );
inputDevice = Pa_GetDefaultInputDeviceID();
}
#else
printf("Using default input device.\n");
inputDevice = Pa_GetDefaultInputDeviceID();
#endif
pdi = Pa_GetDeviceInfo( inputDevice );
if( pdi == NULL )
{
printf("Could not get device info!\n");
goto error;
}
data.numChannels = pdi->maxInputChannels;
printf("Input Device name is %s\n", pdi->name );
printf("Input Device has %d channels.\n", pdi->maxInputChannels);
err = Pa_OpenStream(
&stream,
inputDevice,
pdi->maxInputChannels,
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
2,
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
data.liveChannel = 0;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
for( i=0; i<data.numChannels; i++ )
{
data.liveChannel = i;
printf("Channel %d being sent to output. Hit ENTER for next channel.", i );
fflush(stdout);
getchar();
}
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,144 @@
/*
* $Id$
* debug_multi_out.c
* Play a different sine wave on each channels,
* using the Portable Audio api.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#define FREQ_INCR (300.0 / SAMPLE_RATE)
#define MAX_CHANNELS (64)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
typedef struct
{
int numChannels;
double phases[MAX_CHANNELS];
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int frameIndex, channelIndex;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
{
for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
{
/* Output sine wave on every channel. */
*out++ = (float) sin(data->phases[channelIndex]);
/* Play each channel at a higher frequency. */
data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex);
if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI);
}
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
const PaDeviceInfo *pdi;
paTestData data = {0};
printf("PortAudio Test: output sine wave on each channel.\n" );
err = Pa_Initialize();
if( err != paNoError ) goto error;
pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE );
data.numChannels = pdi->maxOutputChannels;
if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS;
printf("Number of Channels = %d\n", data.numChannels );
err = Pa_OpenStream(
&stream,
paNoDevice, /* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
data.numChannels,
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Hit ENTER to stop sound.\n");
fflush(stdout);
getchar();
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
Pa_CloseStream( stream );
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,339 @@
/*
* $Id$
* debug_record.c
* Record input into an array.
* Save array to a file.
* Based on patest_record.c but with various ugly debug hacks thrown in.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <stdlib.h>
#include <memory.h>
#include "portaudio.h"
#define SAMPLE_RATE (22050)
#define NUM_SECONDS (10)
#define SLEEP_DUR_MSEC (200)
#define FRAMES_PER_BUFFER (1<<10)
#define NUM_REC_BUFS (0)
#if 1
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#else
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#endif
typedef struct
{
long frameIndex; /* Index into sample array. */
long maxFrameIndex;
long samplesPerFrame;
long numSamples;
SAMPLE *recordedSamples;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int recordCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = (SAMPLE*)inputBuffer;
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
long framesToCalc;
unsigned long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
(void) outputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
if( framesLeft < framesPerBuffer )
{
framesToCalc = framesLeft;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
finished = 0;
}
if( inputBuffer == NULL )
{
for( i=0; i<framesToCalc; i++ )
{
*wptr++ = 0; /* left */
*wptr++ = 0; /* right */
}
}
else
{
for( i=0; i<framesToCalc; i++ )
{
*wptr++ = *rptr++; /* left */
*wptr++ = *rptr++; /* right */
}
}
data->frameIndex += framesToCalc;
return finished;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int playCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
SAMPLE *wptr = (SAMPLE*)outputBuffer;
unsigned long i;
int finished;
unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
if( outputBuffer == NULL ) return 0;
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
if( framesLeft < framesPerBuffer )
{
/* final buffer... */
for( i=0; i<framesLeft; i++ )
{
*wptr++ = *rptr++; /* left */
*wptr++ = *rptr++; /* right */
}
for( ; i<framesPerBuffer; i++ )
{
*wptr++ = 0; /* left */
*wptr++ = 0; /* right */
}
data->frameIndex += framesLeft;
finished = 1;
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*wptr++ = *rptr++; /* left */
*wptr++ = *rptr++; /* right */
}
data->frameIndex += framesPerBuffer;
finished = 0;
}
return finished;
}
/****************************************************************/
PaError TestRecording( paTestData *dataPtr )
{
PortAudioStream *stream;
PaError err;
int i;
/* Record some audio. */
err = Pa_OpenStream(
&stream,
Pa_GetDefaultInputDeviceID(),
dataPtr->samplesPerFrame, /* stereo input */
PA_SAMPLE_TYPE,
NULL,
paNoDevice,
0,
PA_SAMPLE_TYPE,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
recordCallback,
dataPtr );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Now recording!\n"); fflush(stdout);
for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
{
if( Pa_StreamActive( stream ) <= 0)
{
printf("Stream inactive!\n");
break;
}
if( dataPtr->maxFrameIndex <= dataPtr->frameIndex )
{
printf("Buffer recording complete.\n");
break;
}
Pa_Sleep(100);
printf("index = %d\n", dataPtr->frameIndex ); fflush(stdout);
}
printf("Finished loop. Close stream.\n"); fflush(stdout);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
printf("Done.\n"); fflush(stdout);
{
SAMPLE max = 0;
SAMPLE posVal;
int i;
for( i=0; i<dataPtr->numSamples; i++ )
{
posVal = dataPtr->recordedSamples[i];
if( posVal < 0 ) posVal = -posVal;
if( posVal > max ) max = posVal;
}
printf("Largest recorded sample = %d\n", max );
}
/* Write recorded data to a file. */
#if 0
{
FILE *fid;
fid = fopen("recorded.raw", "wb");
if( fid == NULL )
{
printf("Could not open file.");
}
else
{
fwrite( dataPtr->recordedSamples, dataPtr->samplesPerFrame * sizeof(SAMPLE), totalFrames, fid );
fclose( fid );
printf("Wrote data to 'recorded.raw'\n");
}
}
#endif
error:
return err;
}
/****************************************************************/
PaError TestPlayback( paTestData *dataPtr )
{
PortAudioStream *stream;
PaError err;
int i;
/* Playback recorded data. */
dataPtr->frameIndex = 0;
printf("Begin playback.\n"); fflush(stdout);
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* NO input */
PA_SAMPLE_TYPE,
NULL,
Pa_GetDefaultOutputDeviceID(),
dataPtr->samplesPerFrame, /* stereo output */
PA_SAMPLE_TYPE,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
playCallback,
dataPtr );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting for playback to finish.\n"); fflush(stdout);
for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
{
Pa_Sleep(100);
printf("index = %d\n", dataPtr->frameIndex );
}
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
error:
return err;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError err;
paTestData data;
long totalFrames;
long numBytes;
long i;
printf("patest_record.c\n"); fflush(stdout);
data.frameIndex = 0;
data.samplesPerFrame = 2;
data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE;
printf("totalFrames = %d\n", totalFrames ); fflush(stdout);
data.numSamples = totalFrames * data.samplesPerFrame;
numBytes = data.numSamples * sizeof(SAMPLE);
data.recordedSamples = (SAMPLE *) malloc( numBytes );
if( data.recordedSamples == NULL )
{
printf("Could not allocate record array.\n");
exit(1);
}
for( i=0; i<data.numSamples; i++ ) data.recordedSamples[i] = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
for( i=0; i<2; i++ )
{
err = TestRecording( &data );
if( err != paNoError ) goto error;
err = TestPlayback( &data );
if( err != paNoError ) goto error;
}
free( data.recordedSamples );
Pa_Terminate();
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
if( err == paHostError )
{
fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() );
}
return -1;
}

View file

@ -0,0 +1,351 @@
/*
* $Id$
* debug_record_reuse.c
* Record input into an array.
* Save array to a file.
* Based on patest_record.c but with various ugly debug hacks thrown in.
* Loop twice and reuse same streams.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <stdlib.h>
#include <memory.h>
#include "portaudio.h"
#define SAMPLE_RATE (22050)
#define NUM_SECONDS (4)
#define SLEEP_DUR_MSEC (200)
#define FRAMES_PER_BUFFER (256)
#define NUM_REC_BUFS (0)
#if 1
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#else
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#endif
typedef struct
{
long frameIndex; /* Index into sample array. */
long maxFrameIndex;
long samplesPerFrame;
long numSamples;
PortAudioStream *outputStream;
PortAudioStream *inputStream;
SAMPLE *recordedSamples;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int recordCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = (SAMPLE*)inputBuffer;
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
long framesToCalc;
unsigned long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
(void) outputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
if( framesLeft < framesPerBuffer )
{
framesToCalc = framesLeft;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
finished = 0;
}
if( inputBuffer == NULL )
{
for( i=0; i<framesToCalc; i++ )
{
*wptr++ = 0; /* left */
*wptr++ = 0; /* right */
}
}
else
{
for( i=0; i<framesToCalc; i++ )
{
*wptr++ = *rptr++; /* left */
*wptr++ = *rptr++; /* right */
}
}
data->frameIndex += framesToCalc;
return finished;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int playCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame];
SAMPLE *wptr = (SAMPLE*)outputBuffer;
unsigned long i;
int finished;
unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
if( outputBuffer == NULL ) return 0;
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
if( framesLeft < framesPerBuffer )
{
/* final buffer... */
for( i=0; i<framesLeft; i++ )
{
*wptr++ = *rptr++; /* left */
*wptr++ = *rptr++; /* right */
}
for( ; i<framesPerBuffer; i++ )
{
*wptr++ = 0; /* left */
*wptr++ = 0; /* right */
}
data->frameIndex += framesLeft;
finished = 1;
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*wptr++ = *rptr++; /* left */
*wptr++ = *rptr++; /* right */
}
data->frameIndex += framesPerBuffer;
finished = 0;
}
return finished;
}
/****************************************************************/
PaError TestRecording( paTestData *dataPtr )
{
PaError err;
int i;
int lastIndex = 0;
/* Open input stream if not already open. */
if( dataPtr->inputStream == NULL )
{
/* Record some audio. */
err = Pa_OpenStream(
&dataPtr->inputStream,
Pa_GetDefaultInputDeviceID(),
dataPtr->samplesPerFrame, /* stereo input */
PA_SAMPLE_TYPE,
NULL,
paNoDevice,
0,
PA_SAMPLE_TYPE,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
recordCallback,
dataPtr );
if( err != paNoError ) goto error;
}
dataPtr->frameIndex = 0;
err = Pa_StartStream( dataPtr->inputStream );
if( err != paNoError ) goto error;
printf("Now recording!\n"); fflush(stdout);
for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
{
int frameIndex, delta;
Pa_Sleep(SLEEP_DUR_MSEC);
frameIndex = dataPtr->frameIndex;
if( Pa_StreamActive( dataPtr->inputStream ) <= 0)
{
printf("Stream inactive!\n");
break;
}
if( dataPtr->maxFrameIndex <= frameIndex )
{
printf("Buffer recording complete.\n");
break;
}
delta = frameIndex - lastIndex;
lastIndex = frameIndex;
printf("index = %d, delta = %d\n", frameIndex, delta ); fflush(stdout);
}
err = Pa_StopStream( dataPtr->inputStream );
if( err != paNoError ) goto error;
printf("Done.\n"); fflush(stdout);
error:
return err;
}
/****************************************************************/
PaError TestPlayback( paTestData *dataPtr )
{
PaError err;
int i;
int lastIndex = 0;
/* Playback recorded data. */
dataPtr->frameIndex = 0;
printf("Begin playback.\n"); fflush(stdout);
/* Open output stream if not already open. */
if( dataPtr->outputStream == NULL )
{
err = Pa_OpenStream(
&dataPtr->outputStream,
paNoDevice,
0, /* NO input */
PA_SAMPLE_TYPE,
NULL,
Pa_GetDefaultOutputDeviceID(),
dataPtr->samplesPerFrame, /* stereo output */
PA_SAMPLE_TYPE,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
playCallback,
dataPtr );
if( err != paNoError ) goto error;
}
err = Pa_StartStream( dataPtr->outputStream );
if( err != paNoError ) goto error;
printf("Waiting for playback to finish.\n"); fflush(stdout);
for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
{
int frameIndex, delta;
Pa_Sleep(SLEEP_DUR_MSEC);
frameIndex = dataPtr->frameIndex;
delta = frameIndex - lastIndex;
lastIndex = frameIndex;
printf("index = %d, delta = %d\n", frameIndex, delta ); fflush(stdout);
}
err = Pa_StopStream( dataPtr->outputStream );
if( err != paNoError ) goto error;
error:
return err;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError err;
paTestData data = { 0 };
long totalFrames;
long numBytes;
long i;
printf("patest_record.c\n"); fflush(stdout);
/* Set up test data structure and sample array. */
data.frameIndex = 0;
data.samplesPerFrame = 2;
data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE;
printf("totalFrames = %d\n", totalFrames ); fflush(stdout);
data.numSamples = totalFrames * data.samplesPerFrame;
numBytes = data.numSamples * sizeof(SAMPLE);
data.recordedSamples = (SAMPLE *) malloc( numBytes );
if( data.recordedSamples == NULL )
{
printf("Could not allocate record array.\n");
exit(1);
}
for( i=0; i<data.numSamples; i++ ) data.recordedSamples[i] = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
/* Record and playback multiple times. */
for( i=0; i<2; i++ )
{
err = TestRecording( &data );
if( err != paNoError ) goto error;
err = TestPlayback( &data );
if( err != paNoError ) goto error;
}
/* Clean up. */
err = Pa_CloseStream( data.inputStream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( data.outputStream );
if( err != paNoError ) goto error;
if( err != paNoError ) goto error;
free( data.recordedSamples );
Pa_Terminate();
printf("Test complete.\n"); fflush(stdout);
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
if( err == paHostError )
{
fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() );
}
return -1;
}

View file

@ -0,0 +1,192 @@
/*
* $Id$
* debug_sine.c
* Play a sine sweep using the Portable Audio api for several seconds.
* Hacked test for debugging PA.
*
* Author: Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
//#define OUTPUT_DEVICE (11)
#define NUM_SECONDS (8)
#define SLEEP_DUR (800)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#if 0
#define MIN_LATENCY_MSEC (200)
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
#else
#define NUM_BUFFERS (0)
#endif
#define MIN_FREQ (100.0f)
#define MAX_FREQ (4000.0f)
#define FREQ_SCALAR (1.00002f)
#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (400)
typedef struct
{
float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
float phase_increment;
float left_phase;
float right_phase;
unsigned int framesToGo;
}
paTestData;
/* Convert phase between and 1.0 to sine value
* using linear interpolation.
*/
float LookupSine( paTestData *data, float phase );
float LookupSine( paTestData *data, float phase )
{
float fIndex = phase*TABLE_SIZE;
int index = (int) fIndex;
float fract = fIndex - index;
float lo = data->sine[index];
float hi = data->sine[index+1];
float val = lo + fract*(hi-lo);
return val;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int framesToCalc;
int i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
if( data->framesToGo < framesPerBuffer )
{
framesToCalc = data->framesToGo;
data->framesToGo = 0;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
data->framesToGo -= framesPerBuffer;
}
for( i=0; i<framesToCalc; i++ )
{
*out++ = LookupSine(data, data->left_phase); /* left */
*out++ = LookupSine(data, data->right_phase); /* right */
data->left_phase += data->phase_increment;
if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
/* sweep frequency then start over. */
data->phase_increment *= FREQ_SCALAR;
if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ);
}
/* zero remainder of final buffer */
for( ; i<(int)framesPerBuffer; i++ )
{
*out++ = 0; /* left */
*out++ = 0; /* right */
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int totalSamps;
printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS );
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.sine[TABLE_SIZE] = data.sine[0]; // set guard point
data.left_phase = data.right_phase = 0.0;
data.phase_increment = CalcPhaseIncrement(MIN_FREQ);
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
printf("totalSamps = %d\n", totalSamps );
err = Pa_Initialize();
if( err != paNoError ) goto error;
printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Is callback being called?\n");
for( i=0; i<((NUM_SECONDS+1)*1000); i+=SLEEP_DUR )
{
printf("data.framesToGo = %d\n", data.framesToGo ); fflush(stdout);
Pa_Sleep( SLEEP_DUR );
}
/* Stop sound until ENTER hit. */
printf("Call Pa_StopStream()\n");
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,157 @@
/*
* $Id$
* Play a different sine wave on each channels,
* using the Portable Audio api.
* Allos amplitude to be set interactively.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#define FREQ_INCR (300.0 / SAMPLE_RATE)
#define MAX_CHANNELS (64)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
typedef struct
{
int numChannels;
double phases[MAX_CHANNELS];
float amplitude;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int frameIndex, channelIndex;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
{
for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
{
/* Output sine wave on every channel. */
*out++ = (float) ( data->amplitude * sin(data->phases[channelIndex]) );
/* Play each channel at a higher frequency. */
data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex);
if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI);
}
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
char pad[256];
PortAudioStream *stream;
PaError err;
const PaDeviceInfo *pdi;
paTestData data = {0};
printf("PortAudio Test: output sine wave on each channel.\n" );
err = Pa_Initialize();
if( err != paNoError ) goto error;
pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE );
data.numChannels = pdi->maxOutputChannels;
if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS;
printf("Number of Channels = %d\n", data.numChannels );
data.amplitude = 1.0;
err = Pa_OpenStream(
&stream,
paNoDevice, /* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
data.numChannels,
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
do
{
printf("Current amplitude = %f\n", data.amplitude );
printf("Enter new amplitude or 'q' to quit.\n");
fflush(stdout);
gets( pad );
if( pad[0] != 'q' )
{
// I tried to use atof but it seems to be broken on Mac OS X 10.1
float amp;
sscanf( pad, "%f", &amp );
data.amplitude = amp;
}
} while( pad[0] != 'q' );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
Pa_CloseStream( stream );
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,202 @@
/*
* $Id$
* patest_sine_formats.c
* Play a sine wave using the Portable Audio api for several seconds.
* Test various data formats.
*
* Author: Phil Burk
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (10)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#define LEFT_FREQ (SAMPLE_RATE/512.0) /* So we hit 1.0 */
#define RIGHT_FREQ (500.0)
#define AMPLITUDE (1.0)
/* Select ONE format for testing. */
#define TEST_UINT8 (1)
#define TEST_INT8 (0)
#define TEST_INT16 (0)
#define TEST_FLOAT32 (0)
#if TEST_UINT8
#define TEST_FORMAT paUInt8
typedef unsigned char SAMPLE_t;
#define SAMPLE_ZERO (0x80)
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
#define FORMAT_NAME "Unsigned 8 Bit"
#elif TEST_INT8
#define TEST_FORMAT paInt8
typedef char SAMPLE_t;
#define SAMPLE_ZERO (0)
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
#define FORMAT_NAME "Signed 8 Bit"
#elif TEST_INT16
#define TEST_FORMAT paInt16
typedef short SAMPLE_t;
#define SAMPLE_ZERO (0)
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767 * (x)))
#define FORMAT_NAME "Signed 16 Bit"
#elif TEST_FLOAT32
#define TEST_FORMAT paFloat32
typedef float SAMPLE_t;
#define SAMPLE_ZERO (0.0)
#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x))
#define FORMAT_NAME "Float 32 Bit"
#endif
#ifndef M_PI
#define M_PI (3.14159265)
#endif
typedef struct
{
double left_phase;
double right_phase;
unsigned int framesToGo;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE_t *out = (SAMPLE_t *)outputBuffer;
SAMPLE_t sample;
int i;
int framesToCalc;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
if( data->framesToGo < framesPerBuffer )
{
framesToCalc = data->framesToGo;
data->framesToGo = 0;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
data->framesToGo -= framesPerBuffer;
}
for( i=0; i<framesToCalc; i++ )
{
data->left_phase += (LEFT_FREQ / SAMPLE_RATE);
if( data->left_phase > 1.0) data->left_phase -= 1.0;
sample = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. ))); /**/
*out++ = sample;
/* *out++ = sample; /**/
/* *out++ = 0; /**/
data->right_phase += (RIGHT_FREQ / SAMPLE_RATE);
if( data->right_phase > 1.0) data->right_phase -= 1.0;
*out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. ))); /**/
/* *out++ = 0; /* */
}
/* zero remainder of final buffer */
for( ; i<(int)framesPerBuffer; i++ )
{
*out++ = SAMPLE_ZERO; /* left */
*out++ = SAMPLE_ZERO; /* right */
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int totalSamps;
printf("PortAudio Test: output " FORMAT_NAME "\n");
data.left_phase = data.right_phase = 0.0;
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
TEST_FORMAT,
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
TEST_FORMAT,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS );
while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("PortAudio Test Finished: " FORMAT_NAME "\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,265 @@
/*
* $Id$
* debug_record_reuse.c
* Record input into an array.
* Save array to a file.
* Based on patest_record.c but with various ugly debug hacks thrown in.
* Loop twice and reuse same streams.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <stdlib.h>
#include <memory.h>
#include "portaudio.h"
#define EWS88MT_12_REC (1)
#define EWS88MT_12_PLAY (10)
#define SBLIVE_REC (2)
#define SBLIVE_PLAY (11)
#if 0
#define INPUT_DEVICE_ID Pa_GetDefaultInputDeviceID()
#define OUTPUT_DEVICE_ID Pa_GetDefaultOutputDeviceID()
#else
#define INPUT_DEVICE_ID (EWS88MT_12_REC)
#define OUTPUT_DEVICE_ID (SBLIVE_PLAY)
#endif
#define INPUT_SAMPLE_RATE (22050.0)
#define OUTPUT_SAMPLE_RATE (22050.0)
#define NUM_SECONDS (4)
#define SLEEP_DUR_MSEC (1000)
#define FRAMES_PER_BUFFER (64)
#define NUM_REC_BUFS (0)
#define SAMPLES_PER_FRAME (2)
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
typedef struct
{
long frameIndex; /* Index into sample array. */
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int recordCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData *) userData;
(void) outputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
if( inputBuffer != NULL )
{
data->frameIndex += framesPerBuffer;
}
return 0;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int playCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData *) userData;
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
if( outputBuffer != NULL )
{
data->frameIndex += framesPerBuffer;
}
return 0;
}
/****************************************************************/
PaError MeasureStreamRate( PortAudioStream *stream, paTestData *dataPtr, double *ratePtr )
{
PaError err;
int i;
int totalFrames = 0;
int totalMSec = 0;
dataPtr->frameIndex = 0;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ )
{
int delta, endIndex;
int startIndex = dataPtr->frameIndex;
Pa_Sleep(SLEEP_DUR_MSEC);
endIndex = dataPtr->frameIndex;
delta = endIndex - startIndex;
totalFrames += delta;
totalMSec += SLEEP_DUR_MSEC;
printf("index = %d, delta = %d\n", endIndex, delta ); fflush(stdout);
}
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
*ratePtr = (totalFrames * 1000.0) / totalMSec;
error:
return err;
}
void ReportRate( double measuredRate, double expectedRate )
{
double error;
error = (measuredRate - expectedRate) / expectedRate;
error = (error < 0 ) ? -error : error;
printf("Measured rate = %6.1f, expected rate = %6.1f\n",
measuredRate, expectedRate );
if( error > 0.1 )
{
printf("ERROR: unexpected rate! --------------------- ERROR!\n");
}
else
{
printf("SUCCESS: rate within tolerance!\n");
}
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError err;
paTestData data = { 0 };
long i;
double rate;
const PaDeviceInfo *pdi;
PortAudioStream *outputStream;
PortAudioStream *inputStream;
err = Pa_Initialize();
if( err != paNoError ) goto error;
pdi = Pa_GetDeviceInfo( INPUT_DEVICE_ID );
printf("Input device = %s\n", pdi->name );
pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE_ID );
printf("Output device = %s\n", pdi->name );
/* Open input stream. */
err = Pa_OpenStream(
&inputStream,
INPUT_DEVICE_ID,
SAMPLES_PER_FRAME, /* stereo input */
PA_SAMPLE_TYPE,
NULL,
paNoDevice,
0,
PA_SAMPLE_TYPE,
NULL,
INPUT_SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
recordCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&outputStream,
paNoDevice,
0, /* NO input */
PA_SAMPLE_TYPE,
NULL,
OUTPUT_DEVICE_ID,
SAMPLES_PER_FRAME, /* stereo output */
PA_SAMPLE_TYPE,
NULL,
OUTPUT_SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
playCallback,
&data );
if( err != paNoError ) goto error;
/* Record and playback multiple times. */
for( i=0; i<2; i++ )
{
printf("Measuring INPUT ------------------------- \n");
err = MeasureStreamRate( inputStream, &data, &rate );
if( err != paNoError ) goto error;
ReportRate( rate, INPUT_SAMPLE_RATE );
printf("Measuring OUTPUT ------------------------- \n");
err = MeasureStreamRate( outputStream, &data, &rate );
if( err != paNoError ) goto error;
ReportRate( rate, OUTPUT_SAMPLE_RATE );
}
/* Clean up. */
err = Pa_CloseStream( inputStream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( outputStream );
if( err != paNoError ) goto error;
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test complete.\n"); fflush(stdout);
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
if( err == paHostError )
{
fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() );
}
return -1;
}

View file

@ -0,0 +1,114 @@
/*
* $Id$
patest1.c
Ring modulate the audio input with a 441hz sine wave for 20 seconds
using the Portable Audio api
Author: Ross Bencina <rossb@audiomulch.com>
Modifications:
April 5th, 2001 - PLB - Check for NULL inputBuffer.
*/
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#ifndef M_PI
#define M_PI (3.14159265)
#endif
typedef struct
{
float sine[100];
int phase;
int sampsToGo;
}
patest1data;
static int patest1Callback( void *inputBuffer, void *outputBuffer,
unsigned long bufferFrames,
PaTimestamp outTime, void *userData )
{
patest1data *data = (patest1data*)userData;
float *in = (float*)inputBuffer;
float *out = (float*)outputBuffer;
int framesToCalc = bufferFrames;
unsigned long i;
int finished = 0;
if(inputBuffer == NULL) return 0;
if( data->sampsToGo < bufferFrames )
{
finished = 1;
}
for( i=0; i<bufferFrames; i++ )
{
*out++ = *in++;
*out++ = *in++;
if( data->phase >= 100 )
data->phase = 0;
}
data->sampsToGo -= bufferFrames;
/* zero remainder of final buffer if not already done */
for( ; i<bufferFrames; i++ )
{
*out++ = 0; /* left */
*out++ = 0; /* right */
}
return finished;
}
int main(int argc, char* argv[]);
int main(int argc, char* argv[])
{
PaStream *stream;
PaError err;
patest1data data;
int i;
int inputDevice = Pa_GetDefaultInputDeviceID();
int outputDevice = Pa_GetDefaultOutputDeviceID();
/* initialise sinusoidal wavetable */
for( i=0; i<100; i++ )
data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
data.phase = 0;
data.sampsToGo = 44100 * 4; // 20 seconds
/* initialise portaudio subsytem */
Pa_Initialize();
err = Pa_OpenStream(
&stream,
inputDevice,
2, /* stereo input */
paFloat32, /* 32 bit floating point input */
NULL,
outputDevice,
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
44100.,
// 22050, /* half second buffers */
// 4, /* four buffers */
512, /* half second buffers */
0, /* four buffers */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patest1Callback,
&data );
if( err == paNoError )
{
err = Pa_StartStream( stream );
// printf( "Press any key to end.\n" );
// getc( stdin ); //wait for input before exiting
// Pa_AbortStream( stream );
printf( "Waiting for stream to complete...\n" );
while( Pa_StreamActive( stream ) )
Pa_Sleep(1000); /* sleep until playback has finished */
err = Pa_CloseStream( stream );
}
else
{
fprintf( stderr, "An error occured while opening the portaudio stream\n" );
if( err == paHostError )
fprintf( stderr, "Host error number: %d\n", Pa_GetHostError() );
else
fprintf( stderr, "Error number: %d\n", err );
}
Pa_Terminate();
printf( "bye\n" );
return 0;
}

View file

@ -0,0 +1,98 @@
/*
* $Id$
* pa_devs.c
* List available devices.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
/*******************************************************************/
int main(void);
int main(void)
{
int i,j;
int numDevices;
const PaDeviceInfo *pdi;
PaError err;
Pa_Initialize();
numDevices = Pa_CountDevices();
if( numDevices < 0 )
{
printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
err = numDevices;
goto error;
}
printf("Number of devices = %d\n", numDevices );
for( i=0; i<numDevices; i++ )
{
pdi = Pa_GetDeviceInfo( i );
printf("---------------------------------------------- #%d", i );
if( i == Pa_GetDefaultInputDeviceID() ) printf(" DefaultInput");
if( i == Pa_GetDefaultOutputDeviceID() ) printf(" DefaultOutput");
printf("\nName = %s\n", pdi->name );
printf("Max Inputs = %d", pdi->maxInputChannels );
printf(", Max Outputs = %d\n", pdi->maxOutputChannels );
if( pdi->numSampleRates == -1 )
{
printf("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] );
}
else
{
printf("Sample Rates =");
for( j=0; j<pdi->numSampleRates; j++ )
{
printf(" %8.2f,", pdi->sampleRates[j] );
}
printf("\n");
}
printf("Native Sample Formats = ");
if( pdi->nativeSampleFormats & paInt8 ) printf("paInt8, ");
if( pdi->nativeSampleFormats & paUInt8 ) printf("paUInt8, ");
if( pdi->nativeSampleFormats & paInt16 ) printf("paInt16, ");
if( pdi->nativeSampleFormats & paInt32 ) printf("paInt32, ");
if( pdi->nativeSampleFormats & paFloat32 ) printf("paFloat32, ");
if( pdi->nativeSampleFormats & paInt24 ) printf("paInt24, ");
if( pdi->nativeSampleFormats & paPackedInt24 ) printf("paPackedInt24, ");
printf("\n");
}
Pa_Terminate();
printf("----------------------------------------------\n");
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,155 @@
/*
* $Id$
* pa_fuzz.c
* Distort input like a fuzz boz.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
/*
** Note that many of the older ISA sound cards on PCs do NOT support
** full duplex audio (simultaneous record and playback).
** And some only support full duplex at lower sample rates.
*/
#define SAMPLE_RATE (44100)
#define PA_SAMPLE_TYPE paFloat32
#define FRAMES_PER_BUFFER (64)
typedef float SAMPLE;
float CubicAmplifier( float input );
static int fuzzCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/* Non-linear amplifier with soft distortion curve. */
float CubicAmplifier( float input )
{
float output, temp;
if( input < 0.0 )
{
temp = input + 1.0f;
output = (temp * temp * temp) - 1.0f;
}
else
{
temp = input - 1.0f;
output = (temp * temp * temp) + 1.0f;
}
return output;
}
#define FUZZ(x) CubicAmplifier(CubicAmplifier(CubicAmplifier(CubicAmplifier(x))))
static int gNumNoInputs = 0;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int fuzzCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
SAMPLE *out = (SAMPLE*)outputBuffer;
SAMPLE *in = (SAMPLE*)inputBuffer;
unsigned int i;
(void) outTime; /* Prevent unused variable warnings. */
(void) userData;
if( inputBuffer == NULL )
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = 0; /* left - silent */
*out++ = 0; /* right - silent */
}
gNumNoInputs += 1;
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = FUZZ(*in++); /* left - distorted */
*out++ = *in++; /* right - clean */
}
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
Pa_GetDefaultInputDeviceID(), /* default output device */
2, /* stereo input */
PA_SAMPLE_TYPE,
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
PA_SAMPLE_TYPE,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0, /* number of buffers, if zero then use default minimum */
0, // paClipOff, /* we won't output out of range samples so don't bother clipping them */
fuzzCallback,
NULL );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Hit ENTER to stop program.\n");
getchar();
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
printf("Finished. gNumNoInputs = %d\n", gNumNoInputs );
Pa_Terminate();
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return -1;
}

View file

@ -0,0 +1,172 @@
/*
* $Id$
* paminlat.c
* Experiment with different numbers of buffers to determine the
* minimum latency for a computer.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <stdlib.h>
#include <math.h>
#include "portaudio.h"
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TWOPI (M_PI * 2.0)
#define DEFAULT_BUFFER_SIZE (64)
typedef struct
{
double left_phase;
double right_phase;
}
paTestData;
/* Very simple synthesis routine to generate two sine waves. */
static int paminlatCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned int i;
double left_phaseInc = 0.02;
double right_phaseInc = 0.06;
double left_phase = data->left_phase;
double right_phase = data->right_phase;
for( i=0; i<framesPerBuffer; i++ )
{
left_phase += left_phaseInc;
if( left_phase > TWOPI ) left_phase -= TWOPI;
*out++ = (float) sin( left_phase );
right_phase += right_phaseInc;
if( right_phase > TWOPI ) right_phase -= TWOPI;
*out++ = (float) sin( right_phase );
}
data->left_phase = left_phase;
data->right_phase = right_phase;
return 0;
}
void main( int argc, char **argv );
void main( int argc, char **argv )
{
PortAudioStream *stream;
PaError err;
paTestData data;
int go;
int numBuffers = 0;
int minBuffers = 0;
int framesPerBuffer;
double sampleRate = 44100.0;
char str[256];
printf("paminlat - Determine minimum latency for your computer.\n");
printf(" usage: paminlat {framesPerBuffer}\n");
printf(" for example: paminlat 256\n");
printf("Adjust your stereo until you hear a smooth tone in each speaker.\n");
printf("Then try to find the smallest number of buffers that still sounds smooth.\n");
printf("Note that the sound will stop momentarily when you change the number of buffers.\n");
/* Get bufferSize from command line. */
framesPerBuffer = ( argc > 1 ) ? atol( argv[1] ) : DEFAULT_BUFFER_SIZE;
printf("Frames per buffer = %d\n", framesPerBuffer );
data.left_phase = data.right_phase = 0.0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
/* Ask PortAudio for the recommended minimum number of buffers. */
numBuffers = minBuffers = Pa_GetMinNumBuffers( framesPerBuffer, sampleRate );
printf("NumBuffers set to %d based on a call to Pa_GetMinNumBuffers()\n", numBuffers );
/* Try different numBuffers in a loop. */
go = 1;
while( go )
{
printf("Latency = framesPerBuffer * numBuffers = %d * %d = %d frames = %d msecs.\n",
framesPerBuffer, numBuffers, framesPerBuffer*numBuffers,
(int)((1000 * framesPerBuffer * numBuffers) / sampleRate) );
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
sampleRate,
framesPerBuffer,
numBuffers, /* number of buffers */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
paminlatCallback,
&data );
if( err != paNoError ) goto error;
if( stream == NULL ) goto error;
/* Start audio. */
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
/* Ask user for a new number of buffers. */
printf("\nMove windows around to see if the sound glitches.\n");
printf("NumBuffers currently %d, enter new number, or 'q' to quit: ", numBuffers );
gets( str );
if( str[0] == 'q' ) go = 0;
else
{
numBuffers = atol( str );
if( numBuffers < minBuffers )
{
printf( "numBuffers below minimum of %d! Set to minimum!!!\n", minBuffers );
numBuffers = minBuffers;
}
}
/* Stop sound until ENTER hit. */
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
}
printf("A good setting for latency would be somewhat higher than\n");
printf("the minimum latency that worked.\n");
printf("PortAudio: Test finished.\n");
Pa_Terminate();
return;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
}

View file

@ -0,0 +1,317 @@
/*
* $Id$
* paqa_devs.c
* Self Testing Quality Assurance app for PortAudio
* Try to open each device and run through all the
* possible configurations.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#include "pa_trace.h"
/****************************************** Definitions ***********/
#define MODE_INPUT (0)
#define MODE_OUTPUT (1)
typedef struct PaQaData
{
unsigned long framesLeft;
int numChannels;
int bytesPerSample;
int mode;
short sawPhase;
PaSampleFormat format;
}
PaQaData;
/****************************************** Prototypes ***********/
static void TestDevices( int mode );
static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
int numChannels );
static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
int numChannels, PaSampleFormat format );
static int QaCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/****************************************** Globals ***********/
static int gNumPassed = 0;
static int gNumFailed = 0;
/****************************************** Macros ***********/
/* Print ERROR if it fails. Tally success or failure. */
/* Odd do-while wrapper seems to be needed for some compilers. */
#define EXPECT(_exp) \
do \
{ \
if ((_exp)) {\
/* printf("SUCCESS for %s\n", #_exp ); */ \
gNumPassed++; \
} \
else { \
printf("ERROR - 0x%x - %s for %s\n", result, \
((result == 0) ? "-" : Pa_GetErrorText(result)), \
#_exp ); \
gNumFailed++; \
goto error; \
} \
} while(0)
/*******************************************************************/
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int QaCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
unsigned long i;
short phase;
PaQaData *data = (PaQaData *) userData;
(void) inputBuffer;
(void) outTime;
/* Play simle sawtooth wave. */
if( data->mode == MODE_OUTPUT )
{
phase = data->sawPhase;
switch( data->format )
{
case paFloat32:
{
float *out = (float *) outputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
phase += 0x123;
*out++ = (float) (phase * (1.0 / 32768.0));
if( data->numChannels == 2 )
{
*out++ = (float) (phase * (1.0 / 32768.0));
}
}
}
break;
case paInt32:
{
int *out = (int *) outputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
phase += 0x123;
*out++ = ((int) phase ) << 16;
if( data->numChannels == 2 )
{
*out++ = ((int) phase ) << 16;
}
}
}
break;
case paInt16:
{
short *out = (short *) outputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
phase += 0x123;
*out++ = phase;
if( data->numChannels == 2 )
{
*out++ = phase;
}
}
}
break;
default:
{
unsigned char *out = (unsigned char *) outputBuffer;
unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
for( i=0; i<numBytes; i++ )
{
*out++ = 0;
}
}
break;
}
data->sawPhase = phase;
}
/* Are we through yet? */
if( data->framesLeft > framesPerBuffer )
{
AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
data->framesLeft -= framesPerBuffer;
return 0;
}
else
{
AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft );
data->framesLeft = 0;
return 1;
}
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError result;
EXPECT( ((result=Pa_Initialize()) == 0) );
printf("Test OUTPUT ---------------\n");
TestDevices( MODE_OUTPUT );
printf("Test INPUT ---------------\n");
TestDevices( MODE_INPUT );
error:
Pa_Terminate();
printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
}
/*******************************************************************
* Try each output device, through its full range of capabilities. */
static void TestDevices( int mode )
{
int id,jc,kr;
int maxChannels;
const PaDeviceInfo *pdi;
int numDevices = Pa_CountDevices();
/* Iterate through all devices. */
for( id=0; id<numDevices; id++ )
{
pdi = Pa_GetDeviceInfo( id );
/* Try 1 to maxChannels on each device. */
maxChannels = ( mode == MODE_INPUT ) ? pdi->maxInputChannels : pdi->maxOutputChannels;
for( jc=1; jc<=maxChannels; jc++ )
{
printf("Name = %s\n", pdi->name );
/* Try each legal sample rate. */
if( pdi->numSampleRates == -1 )
{
double low, high;
low = pdi->sampleRates[0];
high = pdi->sampleRates[1];
if( low < 8000.0 ) low = 8000.0;
TestFormats( mode, id, low, jc );
#define TESTSR(sr) {if(((sr)>=low) && ((sr)<=high)) TestFormats( mode, id, (sr), jc ); }
TESTSR(11025.0);
TESTSR(22050.0);
TESTSR(44100.0);
TestFormats( mode, id, high, jc );
}
else
{
for( kr=0; kr<pdi->numSampleRates; kr++ )
{
TestFormats( mode, id, pdi->sampleRates[kr], jc );
}
}
}
}
}
/*******************************************************************/
static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
int numChannels )
{
TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); /* */
TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); /* */
TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); /* */
/* TestAdvance( mode, deviceID, sampleRate, numChannels, paInt24 ); */
}
/*******************************************************************/
static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
int numChannels, PaSampleFormat format )
{
PortAudioStream *stream = NULL;
PaError result;
PaQaData myData;
#define FRAMES_PER_BUFFER (64)
printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %d -------\n",
( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT",
deviceID, sampleRate, numChannels, format);
/* Setup data for synthesis thread. */
myData.framesLeft = (unsigned long) (sampleRate * 100); /* 100 seconds */
myData.numChannels = numChannels;
myData.mode = mode;
myData.format = format;
switch( format )
{
case paFloat32:
case paInt32:
case paInt24:
myData.bytesPerSample = 4;
break;
case paPackedInt24:
myData.bytesPerSample = 3;
break;
default:
myData.bytesPerSample = 2;
break;
}
EXPECT( ((result = Pa_OpenStream(
&stream,
( mode == MODE_INPUT ) ? deviceID : paNoDevice,
( mode == MODE_INPUT ) ? numChannels : 0,
format,
NULL,
( mode == MODE_OUTPUT ) ? deviceID : paNoDevice,
( mode == MODE_OUTPUT ) ? numChannels : 0,
format,
NULL,
sampleRate,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
QaCallback,
&myData )
) == 0) );
if( stream )
{
PaTimestamp oldStamp, newStamp;
unsigned long oldFrames;
int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400;
int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate);
if( msec < minDelay ) msec = minDelay;
printf("msec = %d\n", msec); /**/
EXPECT( ((result=Pa_StartStream( stream )) == 0) );
/* Check to make sure PortAudio is advancing timeStamp. */
result = paNoError;
oldStamp = Pa_StreamTime(stream);
Pa_Sleep(msec);
newStamp = Pa_StreamTime(stream);
printf("oldStamp = %g,newStamp = %g\n", oldStamp, newStamp ); /**/
EXPECT( (oldStamp < newStamp) );
/* Check to make sure callback is decrementing framesLeft. */
oldFrames = myData.framesLeft;
Pa_Sleep(msec);
printf("oldFrames = %d, myData.framesLeft = %d\n", oldFrames, myData.framesLeft ); /**/
EXPECT( (oldFrames > myData.framesLeft) );
EXPECT( ((result=Pa_CloseStream( stream )) == 0) );
stream = NULL;
}
error:
if( stream != NULL ) Pa_CloseStream( stream );
return result;
}

View file

@ -0,0 +1,330 @@
/*
* $Id$
* paqa_devs.c
* Self Testing Quality Assurance app for PortAudio
* Do lots of bad things to test error reporting.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
/****************************************** Definitions ***********/
#define MODE_INPUT (0)
#define MODE_OUTPUT (1)
#define FRAMES_PER_BUFFER (64)
#define SAMPLE_RATE (44100.0)
#define NUM_BUFFERS (0)
typedef struct PaQaData
{
unsigned long framesLeft;
int numChannels;
int bytesPerSample;
int mode;
}
PaQaData;
/****************************************** Prototypes ***********/
static void TestDevices( int mode );
static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
int numChannels );
static int TestBadOpens( void );
static int TestBadActions( void );
static int QaCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/****************************************** Globals ***********/
static int gNumPassed = 0;
static int gNumFailed = 0;
/****************************************** Macros ***********/
/* Print ERROR if it fails. Tally success or failure. */
/* Odd do-while wrapper seems to be needed for some compilers. */
#define EXPECT(_exp) \
do \
{ \
if ((_exp)) {\
gNumPassed++; \
} \
else { \
printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
gNumFailed++; \
goto error; \
} \
} while(0)
#define HOPEFOR(_exp) \
do \
{ \
if ((_exp)) {\
gNumPassed++; \
} \
else { \
printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \
gNumFailed++; \
} \
} while(0)
/*******************************************************************/
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int QaCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
unsigned long i;
unsigned char *out = (unsigned char *) outputBuffer;
PaQaData *data = (PaQaData *) userData;
(void) inputBuffer; /* Prevent "unused variable" warnings. */
(void) outTime;
/* Zero out buffer so we don't hear terrible noise. */
if( data->mode == MODE_OUTPUT )
{
unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample;
for( i=0; i<numBytes; i++ )
{
*out++ = 0;
}
}
/* Are we through yet? */
if( data->framesLeft > framesPerBuffer )
{
data->framesLeft -= framesPerBuffer;
return 0;
}
else
{
data->framesLeft = 0;
return 1;
}
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError result;
EXPECT( ((result=Pa_Initialize()) == 0) );
TestBadOpens();
TestBadActions();
error:
Pa_Terminate();
printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
return 0;
}
/*******************************************************************/
static int TestBadOpens( void )
{
PortAudioStream *stream = NULL;
PaError result;
PaQaData myData;
/* Setup data for synthesis thread. */
myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
myData.numChannels = 1;
myData.mode = MODE_OUTPUT;
HOPEFOR( (/* No devices specified. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
paNoDevice, 0, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidDeviceId) );
HOPEFOR( ( /* Out of range input device specified. */
(result = Pa_OpenStream(
&stream,
Pa_CountDevices(), 0, paFloat32, NULL,
paNoDevice, 0, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidDeviceId) );
HOPEFOR( ( /* Out of range output device specified. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
Pa_CountDevices(), 0, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidDeviceId) );
HOPEFOR( ( /* Zero input channels. */
(result = Pa_OpenStream(
&stream,
Pa_GetDefaultInputDeviceID(), 0, paFloat32, NULL,
paNoDevice, 0, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidChannelCount) );
HOPEFOR( ( /* Zero output channels. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
Pa_GetDefaultOutputDeviceID(), 0, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidChannelCount) );
HOPEFOR( ( /* Nonzero input channels but no device. */
(result = Pa_OpenStream(
&stream,
Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL,
paNoDevice, 2, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidChannelCount) );
HOPEFOR( ( /* Nonzero output channels but no device. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 2, paFloat32, NULL,
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidChannelCount) );
HOPEFOR( ( /* NULL stream pointer. */
(result = Pa_OpenStream(
NULL,
paNoDevice, 0, paFloat32, NULL,
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paBadStreamPtr) );
HOPEFOR( ( /* Low sample rate. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
1.0, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidSampleRate) );
HOPEFOR( ( /* High sample rate. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
10000000.0, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidSampleRate) );
HOPEFOR( ( /* NULL callback. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
NULL,
&myData )
) == paNullCallback) );
HOPEFOR( ( /* Bad flag. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
(1<<3),
QaCallback,
&myData )
) == paInvalidFlag) );
#if 0 /* FIXME - this is legal for some implementations. */
HOPEFOR( ( /* Use input device as output device. */
(result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidDeviceId) );
HOPEFOR( ( /* Use output device as input device. */
(result = Pa_OpenStream(
&stream,
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
paNoDevice, 0, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == paInvalidDeviceId) );
#endif
if( stream != NULL ) Pa_CloseStream( stream );
return result;
}
/*******************************************************************/
static int TestBadActions( void )
{
PortAudioStream *stream = NULL;
PaError result;
PaQaData myData;
/* Setup data for synthesis thread. */
myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
myData.numChannels = 1;
myData.mode = MODE_OUTPUT;
/* Default output. */
EXPECT( ((result = Pa_OpenStream(
&stream,
paNoDevice, 0, paFloat32, NULL,
Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS,
paClipOff,
QaCallback,
&myData )
) == 0) );
HOPEFOR( ((result = Pa_StartStream( NULL )) == paBadStreamPtr) );
HOPEFOR( ((result = Pa_StopStream( NULL )) == paBadStreamPtr) );
HOPEFOR( ((result = Pa_StreamActive( NULL )) == paBadStreamPtr) );
HOPEFOR( ((result = Pa_CloseStream( NULL )) == paBadStreamPtr) );
HOPEFOR( ((result = (PaError)Pa_StreamTime( NULL )) != 0) );
HOPEFOR( ((result = (PaError)Pa_GetCPULoad( NULL )) != 0) );
error:
if( stream != NULL ) Pa_CloseStream( stream );
return result;
}

View file

@ -0,0 +1,114 @@
/*
$Id$
patest1.c
Ring modulate the audio input with a sine wave for 20 seconds
using the Portable Audio api
Author: Ross Bencina <rossb@audiomulch.com>
Modifications:
April 5th, 2001 - PLB - Check for NULL inputBuffer.
*/
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#ifndef M_PI
#define M_PI (3.14159265)
#endif
typedef struct
{
float sine[100];
int phase;
int sampsToGo;
}
patest1data;
static int patest1Callback( void *inputBuffer, void *outputBuffer,
unsigned long bufferFrames,
PaTimestamp outTime, void *userData )
{
patest1data *data = (patest1data*)userData;
float *in = (float*)inputBuffer;
float *out = (float*)outputBuffer;
int framesToCalc = bufferFrames;
unsigned long i;
int finished = 0;
/* Check to see if any input data is available. */
if(inputBuffer == NULL) return 0;
if( data->sampsToGo < bufferFrames )
{
framesToCalc = data->sampsToGo;
finished = 1;
}
for( i=0; i<framesToCalc; i++ )
{
*out++ = *in++ * data->sine[data->phase]; /* left */
*out++ = *in++ * data->sine[data->phase++]; /* right */
if( data->phase >= 100 )
data->phase = 0;
}
data->sampsToGo -= framesToCalc;
/* zero remainder of final buffer if not already done */
for( ; i<bufferFrames; i++ )
{
*out++ = 0; /* left */
*out++ = 0; /* right */
}
return finished;
}
int main(int argc, char* argv[]);
int main(int argc, char* argv[])
{
PaStream *stream;
PaError err;
patest1data data;
int i;
int inputDevice = Pa_GetDefaultInputDeviceID();
int outputDevice = Pa_GetDefaultOutputDeviceID();
/* initialise sinusoidal wavetable */
for( i=0; i<100; i++ )
data.sine[i] = sin( ((double)i/100.) * M_PI * 2. );
data.phase = 0;
data.sampsToGo = 44100 * 20; // 20 seconds
/* initialise portaudio subsytem */
Pa_Initialize();
err = Pa_OpenStream(
&stream,
inputDevice,
2, /* stereo input */
paFloat32, /* 32 bit floating point input */
NULL,
outputDevice,
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
44100.,
512, /* small buffers */
0, /* let PA determine number of buffers */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patest1Callback,
&data );
if( err == paNoError )
{
err = Pa_StartStream( stream );
printf( "Press any key to end.\n" );
getc( stdin ); //wait for input before exiting
Pa_AbortStream( stream );
printf( "Waiting for stream to complete...\n" );
while( Pa_StreamActive( stream ) )
Pa_Sleep(1000); /* sleep until playback has finished */
err = Pa_CloseStream( stream );
}
else
{
fprintf( stderr, "An error occured while opening the portaudio stream\n" );
if( err == paHostError )
fprintf( stderr, "Host error number: %d\n", Pa_GetHostError() );
else
fprintf( stderr, "Error number: %d\n", err );
}
Pa_Terminate();
printf( "bye\n" );
return 0;
}

View file

@ -0,0 +1,181 @@
/*
* $Id$
* patest_buffer.c
* Test opening streams with different buffer sizes.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <stdlib.h>
#include <math.h>
#include "portaudio.h"
#define NUM_SECONDS (1)
#define SAMPLE_RATE (44100)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
#define BUFFER_TABLE 9
long buffer_table[] = {200,256,500,512,600, 723, 1000, 1024, 2345};
typedef struct
{
short sine[TABLE_SIZE];
int left_phase;
int right_phase;
unsigned int sampsToGo;
}
paTestData;
PaError TestOnce( int buffersize );
static int patest1Callback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patest1Callback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
short *out = (short*)outputBuffer;
unsigned int i;
int finished = 0;
(void) inputBuffer; /* Prevent "unused variable" warnings. */
(void) outTime;
if( data->sampsToGo < framesPerBuffer )
{
/* final buffer... */
for( i=0; i<data->sampsToGo; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
/* zero remainder of final buffer */
for( ; i<framesPerBuffer; i++ )
{
*out++ = 0; /* left */
*out++ = 0; /* right */
}
finished = 1;
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
data->sampsToGo -= framesPerBuffer;
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
int i;
PaError err;
printf("Test opening streams with different buffer sizes\n\n");
for (i = 0 ; i < BUFFER_TABLE; i++)
{
printf("Buffer size %d\n", buffer_table[i]);
err = TestOnce(buffer_table[i]);
if( err < 0 ) return 0;
}
}
PaError TestOnce( int buffersize )
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int totalSamps;
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
}
data.left_phase = data.right_phase = 0;
data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paInt16, /* sample format */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paInt16, /* sample format */
NULL,
SAMPLE_RATE,
buffersize, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patest1Callback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting for sound to finish.\n");
Pa_Sleep(1000);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
return paNoError;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,156 @@
/*
* $Id$
* patest_clip.c
* Play a sine wave using the Portable Audio api for several seconds
* at an amplitude that would require clipping.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (4)
#define SAMPLE_RATE (44100)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct paTestData
{
float sine[TABLE_SIZE];
float amplitude;
int left_phase;
int right_phase;
}
paTestData;
PaError PlaySine( paTestData *data, unsigned long flags, float amplitude );
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int sineCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
float amplitude = data->amplitude;
unsigned int i;
(void) inputBuffer; /* Prevent "unused variable" warnings. */
(void) outTime;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = amplitude * data->sine[data->left_phase]; /* left */
*out++ = amplitude * data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError err;
paTestData DATA;
int i;
printf("PortAudio Test: output sine wave with and without clipping.\n");
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
printf("\nHalf amplitude. Should sound like sine wave.\n"); fflush(stdout);
err = PlaySine( &DATA, paClipOff | paDitherOff, 0.5f );
if( err < 0 ) goto error;
printf("\nFull amplitude. Should sound like sine wave.\n"); fflush(stdout);
err = PlaySine( &DATA, paClipOff | paDitherOff, 0.999f );
if( err < 0 ) goto error;
printf("\nOver range with clipping and dithering turned OFF. Should sound very nasty.\n");
fflush(stdout);
err = PlaySine( &DATA, paClipOff | paDitherOff, 1.1f );
if( err < 0 ) goto error;
printf("\nOver range with clipping and dithering turned ON. Should sound smoother than previous.\n");
fflush(stdout);
err = PlaySine( &DATA, paNoFlag, 1.1f );
if( err < 0 ) goto error;
printf("\nOver range with paClipOff but dithering ON.\n"
"That forces clipping ON so it should sound the same as previous.\n");
fflush(stdout);
err = PlaySine( &DATA, paClipOff, 1.1f );
if( err < 0 ) goto error;
return 0;
error:
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return 1;
}
/*****************************************************************************/
PaError PlaySine( paTestData *data, unsigned long flags, float amplitude )
{
PortAudioStream *stream;
PaError err;
data->left_phase = data->right_phase = 0;
data->amplitude = amplitude;
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
1024,
0, /* number of buffers, if zero then use default minimum */
flags, /* we won't output out of range samples so don't bother clipping them */
sineCallback,
data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
Pa_Sleep( NUM_SECONDS * 1000 );
printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) );
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
return paNoError;
error:
return err;
}

View file

@ -0,0 +1,152 @@
/*
* $Id$
* patest_dither.c
* Attempt to hear difference between dithered and non-dithered signal.
* This only has an effect if the native format is 16 bit.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (4)
#define SAMPLE_RATE (44100)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct paTestData
{
float sine[TABLE_SIZE];
float amplitude;
int left_phase;
int right_phase;
}
paTestData;
PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude );
static int sineCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int sineCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
float amplitude = data->amplitude;
unsigned int i;
(void) outTime;
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = amplitude * data->sine[data->left_phase]; /* left */
*out++ = amplitude * data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError err;
paTestData DATA;
int i;
float amplitude = 32.0 / (1<<15);
printf("PortAudio Test: output EXTREMELY QUIET sine wave with and without dithering.\n");
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
printf("\nNo treatment..\n"); fflush(stdout);
err = PlaySine( &DATA, paClipOff | paDitherOff, amplitude );
if( err < 0 ) goto error;
printf("\nClip.\n");
fflush(stdout);
err = PlaySine( &DATA, paDitherOff, amplitude );
if( err < 0 ) goto error;
printf("\nClip and Dither.\n");
fflush(stdout);
err = PlaySine( &DATA, paNoFlag, amplitude );
if( err < 0 ) goto error;
return 0;
error:
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return -1;
}
/*****************************************************************************/
PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude )
{
PortAudioStream *stream;
PaError err;
data->left_phase = data->right_phase = 0;
data->amplitude = amplitude;
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
1024,
0, /* number of buffers, if zero then use default minimum */
flags, /* we won't output out of range samples so don't bother clipping them */
sineCallback,
(void *)data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
Pa_Sleep( NUM_SECONDS * 1000 );
printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) );
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
return paNoError;
error:
return err;
}

View file

@ -0,0 +1,151 @@
/*
* $Id$
* Play a sine then hang audio callback to test watchdog.
*
* Authors:
* Ross Bencina <rossb@audiomulch.com>
* Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (1024)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TWOPI (M_PI * 2.0)
typedef struct paTestData
{
int sleepFor;
double phase;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
double phaseInc = 0.02;
double phase = data->phase;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
phase += phaseInc;
if( phase > TWOPI ) phase -= TWOPI;
/* This is not a very efficient way to calc sines. */
*out++ = (float) sin( phase ); /* mono */
}
if( data->sleepFor > 0 )
{
Pa_Sleep( data->sleepFor );
}
data->phase = phase;
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
int i;
paTestData data = {0};
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n",
SAMPLE_RATE, FRAMES_PER_BUFFER );
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
1, /* mono output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
/* Gradually increase sleep time. */
for( i=0; i<10000; i+= 1000 )
{
printf("Sleep for %d milliseconds in audio callback.\n", i );
data.sleepFor = i;
Pa_Sleep( ((i<1000) ? 1000 : i) );
}
printf("Suffer for 10 seconds.\n");
Pa_Sleep( 10000 );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,176 @@
/*
* $Id$
* Hear the latency caused by big buffers.
* Play a sine wave and change frequency based on letter input.
*
* Author: Phil Burk <philburk@softsynth.com>, and Darren Gibbs
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#if 0
#define MIN_LATENCY_MSEC (2000)
#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
#else
#define NUM_BUFFERS (0)
#endif
#define MIN_FREQ (100.0f)
#define CalcPhaseIncrement(freq) ((freq)/SAMPLE_RATE)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (400)
typedef struct
{
float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation
float phase_increment;
float left_phase;
float right_phase;
}
paTestData;
float LookupSine( paTestData *data, float phase );
/* Convert phase between and 1.0 to sine value
* using linear interpolation.
*/
float LookupSine( paTestData *data, float phase )
{
float fIndex = phase*TABLE_SIZE;
int index = (int) fIndex;
float fract = fIndex - index;
float lo = data->sine[index];
float hi = data->sine[index+1];
float val = lo + fract*(hi-lo);
return val;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = LookupSine(data, data->left_phase); /* left */
*out++ = LookupSine(data, data->right_phase); /* right */
data->left_phase += data->phase_increment;
if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f;
data->right_phase += (data->phase_increment * 1.5f); /* fifth above */
if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f;
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int done = 0;
printf("PortAudio Test: enter letter then hit ENTER. numBuffers = %d\n", NUM_BUFFERS );
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = 0.90f * (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.sine[TABLE_SIZE] = data.sine[0]; // set guard point
data.left_phase = data.right_phase = 0.0;
data.phase_increment = CalcPhaseIncrement(MIN_FREQ);
err = Pa_Initialize();
if( err != paNoError ) goto error;
printf("PortAudio Test: output device = %d\n", OUTPUT_DEVICE );
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play ASCII keyboard. Hit 'q' to stop. (Use RETURN key on Mac)\n");
fflush(stdout);
while ( !done )
{
float freq;
int index;
char c;
do
{
c = getchar();
}
while( c < ' '); /* Strip white space and control chars. */
if( c == 'q' ) done = 1;
index = c % 26;
freq = MIN_FREQ + (index * 40.0);
data.phase_increment = CalcPhaseIncrement(freq);
}
printf("Call Pa_StopStream()\n");
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,168 @@
/*
* $Id$
* patest_leftright.c
* Play different tone sine waves that alternate between left and right channel.
* The low tone should be on the left channel.
*
* Authors:
* Ross Bencina <rossb@audiomulch.com>
* Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (8)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (512)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
int toggle;
int countDown;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
if( data->toggle )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = 0; /* right */
}
else
{
*out++ = 0; /* left */
*out++ = data->sine[data->right_phase]; /* right */
}
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
if( data->countDown < 0 )
{
data->countDown = SAMPLE_RATE;
data->toggle = !data->toggle;
}
data->countDown -= framesPerBuffer;
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int timeout;
printf("Play different tone sine waves that alternate between left and right channel.\n");
printf("The low tone should be on the left channel.\n");
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.left_phase = data.right_phase = data.toggle = 0;
data.countDown = SAMPLE_RATE;
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play for several seconds.\n");
timeout = NUM_SECONDS * 4;
while( timeout > 0 )
{
Pa_Sleep( 300 );
timeout -= 1;
}
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,137 @@
/*
* $Id$
* patest_longsine.c
* Play a sine wave using the Portable Audio api until ENTER hit.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define SAMPLE_RATE (44100)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned int i;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
printf("PortAudio Test: output sine wave.\n");
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.left_phase = data.right_phase = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
256, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Hit ENTER to stop program.\n");
getchar();
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,194 @@
/*
* $Id$
* patest_many.c
* Start and stop the PortAudio Driver multiple times.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <stdlib.h>
#include <math.h>
#include "portaudio.h"
#define NUM_SECONDS (1)
#define SAMPLE_RATE (44100)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
short sine[TABLE_SIZE];
int left_phase;
int right_phase;
unsigned int sampsToGo;
}
paTestData;
PaError TestOnce( void );
static int patest1Callback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patest1Callback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
short *out = (short*)outputBuffer;
unsigned int i;
int finished = 0;
(void) inputBuffer; /* Prevent "unused variable" warnings. */
(void) outTime;
if( data->sampsToGo < framesPerBuffer )
{
/* final buffer... */
for( i=0; i<data->sampsToGo; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
/* zero remainder of final buffer */
for( ; i<framesPerBuffer; i++ )
{
*out++ = 0; /* left */
*out++ = 0; /* right */
}
finished = 1;
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
data->sampsToGo -= framesPerBuffer;
}
return finished;
}
/*******************************************************************/
#ifdef MACINTOSH
int main(void);
int main(void)
{
int i;
PaError err;
int numLoops = 10;
printf("Loop %d times.\n", numLoops );
for( i=0; i<numLoops; i++ )
{
printf("Loop %d out of %d.\n", i+1, numLoops );
err = TestOnce();
if( err < 0 ) return 0;
}
}
#else
int main(int argc, char **argv);
int main(int argc, char **argv)
{
PaError err;
int i, numLoops = 10;
if( argc > 1 )
{
numLoops = atoi(argv[1]);
}
for( i=0; i<numLoops; i++ )
{
printf("Loop %d out of %d.\n", i+1, numLoops );
err = TestOnce();
if( err < 0 ) return 1;
}
printf("Test complete.\n");
return 0;
}
#endif
PaError TestOnce( void )
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int totalSamps;
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (short) (32767.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
}
data.left_phase = data.right_phase = 0;
data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paInt16, /* sample format */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paInt16, /* sample format */
NULL,
SAMPLE_RATE,
1024, /* frames per buffer */
8, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patest1Callback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting for sound to finish.\n");
Pa_Sleep(1000);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
return paNoError;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,196 @@
/*
* $Id$
* patest_maxsines.c
* How many sine waves can we calculate and play in less than 80% CPU Load.
*
* Authors:
* Ross Bencina <rossb@audiomulch.com>
* Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define MAX_SINES (500)
#define MAX_USAGE (0.8)
#define SAMPLE_RATE (44100)
#define FREQ_TO_PHASE_INC(freq) (freq/(float)SAMPLE_RATE)
#define MIN_PHASE_INC FREQ_TO_PHASE_INC(200.0f)
#define MAX_PHASE_INC (MIN_PHASE_INC * (1 << 5))
#define FRAMES_PER_BUFFER (512)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TWOPI (M_PI * 2.0)
#define TABLE_SIZE (512)
typedef struct paTestData
{
int numSines;
float sine[TABLE_SIZE + 1]; /* add one for guard point for interpolation */
float phases[MAX_SINES];
}
paTestData;
/* Convert phase between and 1.0 to sine value
* using linear interpolation.
*/
float LookupSine( paTestData *data, float phase );
float LookupSine( paTestData *data, float phase )
{
float fIndex = phase*TABLE_SIZE;
int index = (int) fIndex;
float fract = fIndex - index;
float lo = data->sine[index];
float hi = data->sine[index+1];
float val = lo + fract*(hi-lo);
return val;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
float outSample;
float scaler;
int numForScale;
unsigned long i;
int j;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
/* Detemine amplitude scaling factor */
numForScale = data->numSines;
if( numForScale < 8 ) numForScale = 8; /* prevent pops at beginning */
scaler = 1.0f / numForScale;
for( i=0; i<framesPerBuffer; i++ )
{
float output = 0.0;
float phaseInc = MIN_PHASE_INC;
float phase;
for( j=0; j<data->numSines; j++ )
{
/* Advance phase of next oscillator. */
phase = data->phases[j];
phase += phaseInc;
if( phase >= 1.0 ) phase -= 1.0;
output += LookupSine(data, phase);
data->phases[j] = phase;
phaseInc *= 1.02f;
if( phaseInc > MAX_PHASE_INC ) phaseInc = MIN_PHASE_INC;
}
outSample = (float) (output * scaler);
*out++ = outSample; /* Left */
*out++ = outSample; /* Right */
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
int i;
PortAudioStream *stream;
PaError err;
paTestData data = {0};
double load;
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.sine[TABLE_SIZE] = data.sine[0]; /* set guard point */
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* no input */
paFloat32,
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
/* Play an increasing number of sine waves until we hit MAX_USAGE */
do
{
data.numSines++;
Pa_Sleep( 200 );
load = Pa_GetCPULoad( stream );
printf("numSines = %d, CPU load = %f\n", data.numSines, load );
fflush( stdout );
}
while( (load < MAX_USAGE) && (data.numSines < MAX_SINES) );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,144 @@
/*
* $Id$
* patest_multi_out.c
* Play a different sine wave on each channels,
* using the Portable Audio api.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#define FREQ_INCR (300.0 / SAMPLE_RATE)
#define MAX_CHANNELS (64)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
typedef struct
{
int numChannels;
double phases[MAX_CHANNELS];
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int frameIndex, channelIndex;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
{
for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
{
/* Output sine wave on every channel. */
*out++ = (float) sin(data->phases[channelIndex]);
/* Play each channel at a higher frequency. */
data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex);
if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI);
}
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
const PaDeviceInfo *pdi;
paTestData data = {0};
printf("PortAudio Test: output sine wave on each channel.\n" );
err = Pa_Initialize();
if( err != paNoError ) goto error;
pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE );
data.numChannels = pdi->maxOutputChannels;
if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS;
printf("Number of Channels = %d\n", data.numChannels );
err = Pa_OpenStream(
&stream,
paNoDevice, /* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
data.numChannels,
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Hit ENTER to stop sound.\n");
getchar();
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
Pa_CloseStream( stream );
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,245 @@
/*
* $Id$
patest_pink.c
Generate Pink Noise using Gardner method.
Optimization suggested by James McCartney uses a tree
to select which random value to replace.
x x x x x x x x x x x x x x x x
x x x x x x x x
x x x x
x x
x
Tree is generated by counting trailing zeros in an increasing index.
When the index is zero, no random number is selected.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define PINK_MAX_RANDOM_ROWS (30)
#define PINK_RANDOM_BITS (24)
#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS)
typedef struct
{
long pink_Rows[PINK_MAX_RANDOM_ROWS];
long pink_RunningSum; /* Used to optimize summing of generators. */
int pink_Index; /* Incremented each sample. */
int pink_IndexMask; /* Index wrapped by ANDing with this mask. */
float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */
}
PinkNoise;
/* Prototypes */
static unsigned long GenerateRandomNumber( void );
void InitializePinkNoise( PinkNoise *pink, int numRows );
float GeneratePinkNoise( PinkNoise *pink );
/************************************************************/
/* Calculate pseudo-random 32 bit number based on linear congruential method. */
static unsigned long GenerateRandomNumber( void )
{
/* Change this seed for different random sequences. */
static unsigned long randSeed = 22222;
randSeed = (randSeed * 196314165) + 907633515;
return randSeed;
}
/************************************************************/
/* Setup PinkNoise structure for N rows of generators. */
void InitializePinkNoise( PinkNoise *pink, int numRows )
{
int i;
long pmax;
pink->pink_Index = 0;
pink->pink_IndexMask = (1<<numRows) - 1;
/* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
pink->pink_Scalar = 1.0f / pmax;
/* Initialize rows. */
for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
pink->pink_RunningSum = 0;
}
#define PINK_MEASURE
#ifdef PINK_MEASURE
float pinkMax = -999.0;
float pinkMin = 999.0;
#endif
/* Generate Pink noise values between -1.0 and +1.0 */
float GeneratePinkNoise( PinkNoise *pink )
{
long newRandom;
long sum;
float output;
/* Increment and mask index. */
pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
/* If index is zero, don't update any random values. */
if( pink->pink_Index != 0 )
{
/* Determine how many trailing zeros in PinkIndex. */
/* This algorithm will hang if n==0 so test first. */
int numZeros = 0;
int n = pink->pink_Index;
while( (n & 1) == 0 )
{
n = n >> 1;
numZeros++;
}
/* Replace the indexed ROWS random value.
* Subtract and add back to RunningSum instead of adding all the random
* values together. Only one changes each time.
*/
pink->pink_RunningSum -= pink->pink_Rows[numZeros];
newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
pink->pink_RunningSum += newRandom;
pink->pink_Rows[numZeros] = newRandom;
}
/* Add extra white noise value. */
newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
sum = pink->pink_RunningSum + newRandom;
/* Scale to range of -1.0 to 0.9999. */
output = pink->pink_Scalar * sum;
#ifdef PINK_MEASURE
/* Check Min/Max */
if( output > pinkMax ) pinkMax = output;
else if( output < pinkMin ) pinkMin = output;
#endif
return output;
}
/*******************************************************************/
#define PINK_TEST
#ifdef PINK_TEST
/* Context for callback routine. */
typedef struct
{
PinkNoise leftPink;
PinkNoise rightPink;
unsigned int sampsToGo;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
int finished;
int i;
int numFrames;
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
(void) inputBuffer; /* Prevent "unused variable" warnings. */
(void) outTime;
/* Are we almost at end. */
if( data->sampsToGo < framesPerBuffer )
{
numFrames = data->sampsToGo;
finished = 1;
}
else
{
numFrames = framesPerBuffer;
finished = 0;
}
for( i=0; i<numFrames; i++ )
{
*out++ = GeneratePinkNoise( &data->leftPink );
*out++ = GeneratePinkNoise( &data->rightPink );
}
data->sampsToGo -= numFrames;
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int totalSamps;
/* Initialize two pink noise signals with different numbers of rows. */
InitializePinkNoise( &data.leftPink, 12 );
InitializePinkNoise( &data.rightPink, 16 );
/* Look at a few values. */
{
int i;
float pink;
for( i=0; i<20; i++ )
{
pink = GeneratePinkNoise( &data.leftPink );
printf("Pink = %f\n", pink );
}
}
data.sampsToGo = totalSamps = 8*44100; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
/* Open a stereo PortAudio stream so we can hear the result. */
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
44100.,
2048, /* 46 msec buffers */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting for sound to finish.\n");
while( Pa_StreamActive( stream ) )
{
Pa_Sleep(100); /* SPIN! */
}
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
#ifdef PINK_MEASURE
printf("Pink min = %f, max = %f\n", pinkMin, pinkMax );
#endif
Pa_Terminate();
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return 0;
}
#endif /* PINK_TEST */

View file

@ -0,0 +1,322 @@
/*
* $Id$
* patest_record.c
* Record input into an array.
* Save array to a file.
* Playback recorded data.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <stdlib.h>
#include "portaudio.h"
/* #define SAMPLE_RATE (17932) /* Test failure to open with this value. */
#define SAMPLE_RATE (44100)
#define NUM_SECONDS (5)
#define NUM_CHANNELS (2)
/* #define DITHER_FLAG (paDitherOff) /**/
#define DITHER_FLAG (0) /**/
/* Select sample format. */
#if 0
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#define SAMPLE_SILENCE (0.0f)
#elif 0
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#define SAMPLE_SILENCE (0)
#elif 0
#define PA_SAMPLE_TYPE paInt8
typedef char SAMPLE;
#define SAMPLE_SILENCE (0)
#else
#define PA_SAMPLE_TYPE paUInt8
typedef unsigned char SAMPLE;
#define SAMPLE_SILENCE (128)
#endif
typedef struct
{
int frameIndex; /* Index into sample array. */
int maxFrameIndex;
SAMPLE *recordedSamples;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int recordCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = (SAMPLE*)inputBuffer;
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
long framesToCalc;
long i;
int finished;
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
(void) outputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
if( framesLeft < framesPerBuffer )
{
framesToCalc = framesLeft;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
finished = 0;
}
if( inputBuffer == NULL )
{
for( i=0; i<framesToCalc; i++ )
{
*wptr++ = SAMPLE_SILENCE; /* left */
if( NUM_CHANNELS == 2 ) *wptr++ = SAMPLE_SILENCE; /* right */
}
}
else
{
for( i=0; i<framesToCalc; i++ )
{
*wptr++ = *rptr++; /* left */
if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++; /* right */
}
}
data->frameIndex += framesToCalc;
return finished;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int playCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
SAMPLE *wptr = (SAMPLE*)outputBuffer;
unsigned int i;
int finished;
unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
if( framesLeft < framesPerBuffer )
{
/* final buffer... */
for( i=0; i<framesLeft; i++ )
{
*wptr++ = *rptr++; /* left */
if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++; /* right */
}
for( ; i<framesPerBuffer; i++ )
{
*wptr++ = 0; /* left */
if( NUM_CHANNELS == 2 ) *wptr++ = 0; /* right */
}
data->frameIndex += framesLeft;
finished = 1;
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*wptr++ = *rptr++; /* left */
if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++; /* right */
}
data->frameIndex += framesPerBuffer;
finished = 0;
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int totalFrames;
int numSamples;
int numBytes;
SAMPLE max, average, val;
printf("patest_record.c\n"); fflush(stdout);
data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
data.frameIndex = 0;
numSamples = totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
data.recordedSamples = (SAMPLE *) malloc( numBytes );
if( data.recordedSamples == NULL )
{
printf("Could not allocate record array.\n");
exit(1);
}
for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
/* Record some audio. -------------------------------------------- */
err = Pa_OpenStream(
&stream,
Pa_GetDefaultInputDeviceID(),
NUM_CHANNELS, /* stereo input */
PA_SAMPLE_TYPE,
NULL,
paNoDevice,
0,
PA_SAMPLE_TYPE,
NULL,
SAMPLE_RATE,
1024, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
0, //paDitherOff, /* flags */
recordCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Now recording!!\n"); fflush(stdout);
while( Pa_StreamActive( stream ) )
{
Pa_Sleep(1000);
printf("index = %d\n", data.frameIndex ); fflush(stdout);
}
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
/* Measure maximum peak amplitude. */
max = 0;
average = 0;
for( i=0; i<numSamples; i++ )
{
val = data.recordedSamples[i];
if( val < 0 ) val = -val; /* ABS */
if( val > max )
{
max = val;
}
average += val;
}
average = average / numSamples;
if( PA_SAMPLE_TYPE == paFloat32 )
{
printf("sample max amplitude = %f\n", max );
printf("sample average = %f\n", average );
}
else
{
printf("sample max amplitude = %d\n", max );
printf("sample average = %d\n", average );
}
/* Write recorded data to a file. */
#if 0
{
FILE *fid;
fid = fopen("recorded.raw", "wb");
if( fid == NULL )
{
printf("Could not open file.");
}
else
{
fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid );
fclose( fid );
printf("Wrote data to 'recorded.raw'\n");
}
}
#endif
/* Playback recorded data. -------------------------------------------- */
data.frameIndex = 0;
printf("Begin playback.\n"); fflush(stdout);
err = Pa_OpenStream(
&stream,
paNoDevice,
0, /* NO input */
PA_SAMPLE_TYPE,
NULL,
Pa_GetDefaultOutputDeviceID(),
NUM_CHANNELS, /* stereo output */
PA_SAMPLE_TYPE,
NULL,
SAMPLE_RATE,
1024, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
playCallback,
&data );
if( err != paNoError ) goto error;
if( stream )
{
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting for playback to finish.\n"); fflush(stdout);
while( Pa_StreamActive( stream ) ) Pa_Sleep(100);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
printf("Done.\n"); fflush(stdout);
}
free( data.recordedSamples );
Pa_Terminate();
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return -1;
}

View file

@ -0,0 +1,41 @@
/* $Id$ */
#include "stdio.h"
#include "portaudio.h"
/* This will be called asynchronously by the PortAudio engine. */
static int myCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
{
float *out = (float *) outputBuffer;
float *in = (float *) inputBuffer;
float leftInput, rightInput;
unsigned int i;
if( inputBuffer == NULL ) return 0;
/* Read input buffer, process data, and fill output buffer. */
for( i=0; i<framesPerBuffer; i++ )
{
leftInput = *in++; /* Get interleaved samples from input buffer. */
rightInput = *in++;
*out++ = leftInput * rightInput; /* ring modulation */
*out++ = 0.5f * (leftInput + rightInput); /* mix */
}
return 0;
}
/* Open a PortAudioStream to input and output audio data. */
int main(void)
{
PortAudioStream *stream;
Pa_Initialize();
Pa_OpenDefaultStream(
&stream,
2, 2, /* stereo input and output */
paFloat32, 44100.0,
64, 0, /* 64 frames per buffer, let PA determine numBuffers */
myCallback, NULL );
Pa_StartStream( stream );
Pa_Sleep( 10000 ); /* Sleep for 10 seconds while processing. */
Pa_StopStream( stream );
Pa_CloseStream( stream );
Pa_Terminate();
return 0;
}

View file

@ -0,0 +1,118 @@
/*
* $Id$
* patest_saw.c
* Play a simple sawtooth wave.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (4)
#define SAMPLE_RATE (44100)
typedef struct
{
float left_phase;
float right_phase;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
/* Cast data passed through stream to our structure. */
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned int i;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->left_phase; /* left */
*out++ = data->right_phase; /* right */
/* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
data->left_phase += 0.01f;
/* When signal reaches top, drop back down. */
if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
/* higher pitch so we can distinguish left and right. */
data->right_phase += 0.03f;
if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
}
return 0;
}
/*******************************************************************/
static paTestData data;
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
printf("PortAudio Test: output sawtooth wave.\n");
/* Initialize our data for use by callback. */
data.left_phase = data.right_phase = 0.0;
/* Initialize library before making any other calls. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
/* Open an audio I/O stream. */
err = Pa_OpenDefaultStream(
&stream,
0, /* no input channels */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
SAMPLE_RATE,
256, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
/* Sleep for several seconds. */
Pa_Sleep(NUM_SECONDS*1000);
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,137 @@
/*
* $Id$
* patest_sine.c
* Play a sine wave using the Portable Audio api for several seconds.
*
* Authors:
* Ross Bencina <rossb@audiomulch.com>
* Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (5)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.left_phase = data.right_phase = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play for %d seconds.\n", NUM_SECONDS );
Pa_Sleep( NUM_SECONDS * 1000 );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,184 @@
/*
* $Id$
* patest_sine8.c
* Play a sine wave using the Portable Audio api for several seconds.
* Test 8 bit data.
*
* Author: Ross Bencina <rossb@audiomulch.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (8)
#define SAMPLE_RATE (44100)
#define TEST_UNSIGNED (1)
#if TEST_UNSIGNED
#define TEST_FORMAT paUInt8
#else
#define TEST_FORMAT paInt8
#endif
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
#if TEST_UNSIGNED
unsigned char sine[TABLE_SIZE];
#else
char sine[TABLE_SIZE];
#endif
int left_phase;
int right_phase;
unsigned int framesToGo;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
char *out = (char*)outputBuffer;
int i;
int framesToCalc;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
if( data->framesToGo < framesPerBuffer )
{
framesToCalc = data->framesToGo;
data->framesToGo = 0;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
data->framesToGo -= framesPerBuffer;
}
for( i=0; i<framesToCalc; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
/* zero remainder of final buffer */
for( ; i<(int)framesPerBuffer; i++ )
{
#if TEST_UNSIGNED
*out++ = (unsigned char) 0x80; /* left */
*out++ = (unsigned char) 0x80; /* right */
#else
*out++ = 0; /* left */
*out++ = 0; /* right */
#endif
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int totalSamps;
#if TEST_UNSIGNED
printf("PortAudio Test: output UNsigned 8 bit sine wave.\n");
#else
printf("PortAudio Test: output signed 8 bit sine wave.\n");
#endif
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (char) (127.0 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
#if TEST_UNSIGNED
data.sine[i] += (unsigned char) 0x80;
#endif
}
data.left_phase = data.right_phase = 0;
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
TEST_FORMAT,
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
TEST_FORMAT,
NULL,
SAMPLE_RATE,
256, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
/* Watch until sound is halfway finished. */
while( Pa_StreamTime( stream ) < (totalSamps/2) ) Pa_Sleep(10);
/* Stop sound until ENTER hit. */
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
printf("Pause for 2 seconds.\n");
Pa_Sleep( 2000 );
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting for sound to finish.\n");
while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,195 @@
/*
* $Id$
* patest_sine_formats.c
* Play a sine wave using the Portable Audio api for several seconds.
* Test various data formats.
*
* Author: Phil Burk
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (20)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (512)
#define LEFT_FREQ (SAMPLE_RATE/256.0) /* So we hit 1.0 */
#define RIGHT_FREQ (500.0)
#define AMPLITUDE (1.0)
/* Select ONE format for testing. */
#define TEST_UINT8 (0)
#define TEST_INT8 (0)
#define TEST_INT16 (1)
#define TEST_FLOAT32 (0)
#if TEST_UINT8
#define TEST_FORMAT paUInt8
typedef unsigned char SAMPLE_t;
#define SAMPLE_ZERO (0x80)
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
#define FORMAT_NAME "Unsigned 8 Bit"
#elif TEST_INT8
#define TEST_FORMAT paInt8
typedef char SAMPLE_t;
#define SAMPLE_ZERO (0)
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x)))
#define FORMAT_NAME "Signed 8 Bit"
#elif TEST_INT16
#define TEST_FORMAT paInt16
typedef short SAMPLE_t;
#define SAMPLE_ZERO (0)
#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767 * (x)))
#define FORMAT_NAME "Signed 16 Bit"
#elif TEST_FLOAT32
#define TEST_FORMAT paFloat32
typedef float SAMPLE_t;
#define SAMPLE_ZERO (0.0)
#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x))
#define FORMAT_NAME "Float 32 Bit"
#endif
#ifndef M_PI
#define M_PI (3.14159265)
#endif
typedef struct
{
double left_phase;
double right_phase;
unsigned int framesToGo;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE_t *out = (SAMPLE_t *)outputBuffer;
int i;
int framesToCalc;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
if( data->framesToGo < framesPerBuffer )
{
framesToCalc = data->framesToGo;
data->framesToGo = 0;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
data->framesToGo -= framesPerBuffer;
}
for( i=0; i<framesToCalc; i++ )
{
data->left_phase += (LEFT_FREQ / SAMPLE_RATE);
if( data->left_phase > 1.0) data->left_phase -= 1.0;
*out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. )));
data->right_phase += (RIGHT_FREQ / SAMPLE_RATE);
if( data->right_phase > 1.0) data->right_phase -= 1.0;
*out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. )));
}
/* zero remainder of final buffer */
for( ; i<(int)framesPerBuffer; i++ )
{
*out++ = SAMPLE_ZERO; /* left */
*out++ = SAMPLE_ZERO; /* right */
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int totalSamps;
printf("PortAudio Test: output " FORMAT_NAME "\n");
data.left_phase = data.right_phase = 0.0;
data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
TEST_FORMAT,
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
TEST_FORMAT,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS );
while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("PortAudio Test Finished: " FORMAT_NAME "\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,205 @@
/*
* $Id$
* patest_sine_time.c
* Play a sine wave using the Portable Audio api for several seconds.
* Pausing in the middle.
* use the Pa_StreamTime() and Pa_StreamActive() calls.
*
* Authors:
* Ross Bencina <rossb@audiomulch.com>
* Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (8)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#define NUM_BUFFERS (0)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
int framesToGo;
volatile PaTimestamp outTime;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int i;
int framesToCalc;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
data->outTime = outTime;
if( data->framesToGo < framesPerBuffer )
{
framesToCalc = data->framesToGo;
data->framesToGo = 0;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
data->framesToGo -= framesPerBuffer;
}
for( i=0; i<framesToCalc; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
/* zero remainder of final buffer */
for( ; i<(int)framesPerBuffer; i++ )
{
*out++ = 0; /* left */
*out++ = 0; /* right */
}
return finished;
}
/*******************************************************************/
static void ReportStreamTime( PortAudioStream *stream, paTestData *data );
static void ReportStreamTime( PortAudioStream *stream, paTestData *data )
{
PaTimestamp streamTime, latency, outTime;
streamTime = Pa_StreamTime( stream );
outTime = data->outTime;
if( outTime < 0.0 )
{
printf("Stream time = %8.1f\n", streamTime );
}
else
{
latency = outTime - streamTime;
printf("Stream time = %8.1f, outTime = %8.1f, latency = %8.1f\n",
streamTime, outTime, latency );
}
fflush(stdout);
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData DATA;
int i;
int totalSamps;
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
DATA.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
DATA.left_phase = DATA.right_phase = 0;
DATA.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&DATA );
if( err != paNoError ) goto error;
DATA.outTime = -1.0; // mark time for callback as undefined
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
/* Watch until sound is halfway finished. */
printf("Play for %d seconds.\n", NUM_SECONDS/2 ); fflush(stdout);
do
{
ReportStreamTime( stream, &DATA );
Pa_Sleep(100);
} while( Pa_StreamTime( stream ) < (totalSamps/2) );
/* Stop sound until ENTER hit. */
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
printf("Pause for 2 seconds.\n"); fflush(stdout);
Pa_Sleep( 2000 );
DATA.outTime = -1.0; // mark time for callback as undefined
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play until sound is finished.\n"); fflush(stdout);
do
{
ReportStreamTime( stream, &DATA );
Pa_Sleep(100);
} while( Pa_StreamActive( stream ) );
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,285 @@
/*
* $Id$
* patest_stop.c
*
* Test the three ways of stopping audio:
* calling Pa_StopStream(),
* calling Pa_AbortStream(),
* and returning a 1 from the callback function.
*
* A long latency is set up so that you can hear the difference.
* Then a simple 8 note sequence is repeated twice.
* The program will print what you should hear.
*
* Author: Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
#define SLEEP_DUR (200)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#define LATENCY_MSEC (3000)
#define NUM_BUFFERS ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
#define FRAMES_PER_NOTE (SAMPLE_RATE/2)
#define MAX_REPEATS (2)
#define FUNDAMENTAL (400.0f / SAMPLE_RATE)
#define NOTE_0 (FUNDAMENTAL * 1.0f / 1.0f)
#define NOTE_1 (FUNDAMENTAL * 5.0f / 4.0f)
#define NOTE_2 (FUNDAMENTAL * 4.0f / 3.0f)
#define NOTE_3 (FUNDAMENTAL * 3.0f / 2.0f)
#define NOTE_4 (FUNDAMENTAL * 2.0f / 1.0f)
#define MODE_FINISH (0)
#define MODE_STOP (1)
#define MODE_ABORT (2)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (400)
typedef struct
{
float waveform[TABLE_SIZE + 1]; // add one for guard point for interpolation
float phase_increment;
float phase;
float *tune;
int notesPerTune;
int frameCounter;
int noteCounter;
int repeatCounter;
PaTimestamp outTime;
int stopMode;
int done;
}
paTestData;
/************* Prototypes *****************************/
int TestStopMode( paTestData *data );
float LookupWaveform( paTestData *data, float phase );
/******************************************************
* Convert phase between 0.0 and 1.0 to waveform value
* using linear interpolation.
*/
float LookupWaveform( paTestData *data, float phase )
{
float fIndex = phase*TABLE_SIZE;
int index = (int) fIndex;
float fract = fIndex - index;
float lo = data->waveform[index];
float hi = data->waveform[index+1];
float val = lo + fract*(hi-lo);
return val;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
float value;
unsigned int i = 0;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
data->outTime = outTime;
if( !data->done )
{
for( i=0; i<framesPerBuffer; i++ )
{
/* Are we done with this note? */
if( data->frameCounter >= FRAMES_PER_NOTE )
{
data->noteCounter += 1;
data->frameCounter = 0;
/* Are we done with this tune? */
if( data->noteCounter >= data->notesPerTune )
{
data->noteCounter = 0;
data->repeatCounter += 1;
/* Are we totally done? */
if( data->repeatCounter >= MAX_REPEATS )
{
data->done = 1;
if( data->stopMode == MODE_FINISH )
{
finished = 1;
break;
}
}
}
data->phase_increment = data->tune[data->noteCounter];
}
value = LookupWaveform(data, data->phase);
*out++ = value; /* left */
*out++ = value; /* right */
data->phase += data->phase_increment;
if( data->phase >= 1.0f ) data->phase -= 1.0f;
data->frameCounter += 1;
}
}
/* zero remainder of final buffer */
for( ; i<framesPerBuffer; i++ )
{
*out++ = 0; /* left */
*out++ = 0; /* right */
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
paTestData DATA;
int i;
float simpleTune[] = { NOTE_0, NOTE_1, NOTE_2, NOTE_3, NOTE_4, NOTE_3, NOTE_2, NOTE_1 };
printf("PortAudio Test: play song and test stopping. ask for %d buffers\n", NUM_BUFFERS );
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
DATA.waveform[i] = (float) (
(0.2 * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. )) +
(0.2 * sin( ((double)(3*i)/(double)TABLE_SIZE) * M_PI * 2. )) +
(0.1 * sin( ((double)(5*i)/(double)TABLE_SIZE) * M_PI * 2. ))
);
}
DATA.waveform[TABLE_SIZE] = DATA.waveform[0]; // set guard point
DATA.tune = &simpleTune[0];
DATA.notesPerTune = sizeof(simpleTune) / sizeof(float);
printf("Test MODE_FINISH - callback returns 1.\n");
printf("Should hear entire %d note tune repeated twice.\n", DATA.notesPerTune);
DATA.stopMode = MODE_FINISH;
if( TestStopMode( &DATA ) != paNoError )
{
printf("Test of MODE_FINISH failed!\n");
goto error;
}
printf("Test MODE_STOP - stop when song is done.\n");
printf("Should hear entire %d note tune repeated twice.\n", DATA.notesPerTune);
DATA.stopMode = MODE_STOP;
if( TestStopMode( &DATA ) != paNoError )
{
printf("Test of MODE_STOP failed!\n");
goto error;
}
printf("Test MODE_ABORT - abort immediately.\n");
printf("Should hear last repetition cut short by %d msec.\n", LATENCY_MSEC);
DATA.stopMode = MODE_ABORT;
if( TestStopMode( &DATA ) != paNoError )
{
printf("Test of MODE_ABORT failed!\n");
goto error;
}
return 0;
error:
return 1;
}
int TestStopMode( paTestData *data )
{
PortAudioStream *stream;
PaError err;
data->done = 0;
data->phase = 0.0;
data->frameCounter = 0;
data->noteCounter = 0;
data->repeatCounter = 0;
data->phase_increment = data->tune[data->noteCounter];
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
OUTPUT_DEVICE,
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
if( data->stopMode == MODE_FINISH )
{
while( Pa_StreamActive( stream ) )
{
/*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
data->noteCounter, data->repeatCounter );
fflush(stdout); /**/
Pa_Sleep( SLEEP_DUR );
}
}
else
{
while( data->repeatCounter < MAX_REPEATS )
{
/*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
data->noteCounter, data->repeatCounter );
fflush(stdout); /**/
Pa_Sleep( SLEEP_DUR );
}
}
if( data->stopMode == MODE_ABORT )
{
printf("Call Pa_AbortStream()\n");
err = Pa_AbortStream( stream );
}
else
{
printf("Call Pa_StopStream()\n");
err = Pa_StopStream( stream );
}
if( err != paNoError ) goto error;
printf("Call Pa_CloseStream()\n"); fflush(stdout);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,227 @@
/*
* $Id$
* patest_sync.c
* Test time stamping and synchronization of audio and video.
* A high latency is used so we can hear the difference in time.
* Random durations are used so we know we are hearing the right beep
* and not the one before or after.
*
* Sequence of events:
* Foreground requests a beep.
* Background randomly schedules a beep.
* Foreground waits for the beep to be heard based on Pa_StreamTime().
* Foreground outputs video (printf) in sync with audio.
* Repeat.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_BEEPS (6)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (256)
#define BEEP_DURATION (1000)
#define LATENCY_MSEC (2000)
#define SLEEP_MSEC (10)
#define TIMEOUT_MSEC ((3 * LATENCY_MSEC) / (2 * SLEEP_MSEC))
#define NUM_BUFFERS ((LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000))
#define STATE_BKG_IDLE (0)
#define STATE_BKG_PENDING (1)
#define STATE_BKG_BEEPING (2)
typedef struct
{
float left_phase;
float right_phase;
int state;
int requestBeep; /* Set by foreground, cleared by background. */
PaTimestamp beepTime;
int beepCount;
}
paTestData;
static unsigned long GenerateRandomNumber( void );
/************************************************************/
/* Calculate pseudo-random 32 bit number based on linear congruential method. */
static unsigned long GenerateRandomNumber( void )
{
static unsigned long randSeed = 22222; /* Change this for different random sequences. */
randSeed = (randSeed * 196314165) + 907633515;
return randSeed;
}
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
/* Cast data passed through stream to our structure. */
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned int i;
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
switch( data->state )
{
case STATE_BKG_IDLE:
/* Schedule beep at some random time in the future. */
if( data->requestBeep )
{
int random = GenerateRandomNumber() >> 14;
data->beepTime = outTime + (i + random + (SAMPLE_RATE/4));
data->state = STATE_BKG_PENDING;
data->requestBeep = 0;
data->left_phase = data->right_phase = 0.0;
}
*out++ = 0.0; /* left */
*out++ = 0.0; /* right */
break;
case STATE_BKG_PENDING:
if( (outTime + i) >= data->beepTime )
{
data->state = STATE_BKG_BEEPING;
data->beepCount = BEEP_DURATION;
}
*out++ = 0.0; /* left */
*out++ = 0.0; /* right */
break;
case STATE_BKG_BEEPING:
if( data->beepCount <= 0 )
{
data->state = STATE_BKG_IDLE;
*out++ = 0.0; /* left */
*out++ = 0.0; /* right */
}
else
{
/* Play sawtooth wave. */
*out++ = data->left_phase; /* left */
*out++ = data->right_phase; /* right */
/* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
data->left_phase += 0.01f;
/* When signal reaches top, drop back down. */
if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
/* higher pitch so we can distinguish left and right. */
data->right_phase += 0.03f;
if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
}
data->beepCount -= 1;
break;
default:
data->state = STATE_BKG_IDLE;
break;
}
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData DATA;
int i, timeout;
PaTimestamp previousTime;
printf("PortAudio Test: you should see BEEP at the same time you hear it.\n");
printf("Wait for a few seconds random delay between BEEPs.\n");
printf("BEEP %d times.\n", NUM_BEEPS );
/* Initialize our DATA for use by callback. */
DATA.left_phase = DATA.right_phase = 0.0;
DATA.state = STATE_BKG_IDLE;
DATA.requestBeep = 0;
/* Initialize library before making any other calls. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
/* Open an audio I/O stream. */
err = Pa_OpenDefaultStream(
&stream,
0, /* no input channels */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
SAMPLE_RATE,
FRAMES_PER_BUFFER,
NUM_BUFFERS,
patestCallback,
&DATA );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
previousTime = Pa_StreamTime( stream );
for( i=0; i<NUM_BEEPS; i++ )
{
/* Request a beep from background. */
DATA.requestBeep = 1;
/* Wait for background to acknowledge request. */
timeout = TIMEOUT_MSEC;
while( (DATA.requestBeep == 1) && (timeout-- > 0 ) ) Pa_Sleep(SLEEP_MSEC);
if( timeout <= 0 )
{
fprintf( stderr, "Timed out waiting for background to acknowledge request.\n" );
goto error;
}
/* Wait for scheduled beep time. */
timeout = TIMEOUT_MSEC + (10000/SLEEP_MSEC);
while( (Pa_StreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) )
{
Pa_Sleep(SLEEP_MSEC);
}
if( timeout <= 0 )
{
fprintf( stderr, "Timed out waiting for time. Now = %g, Beep for %g.\n",
Pa_StreamTime( stream ), DATA.beepTime );
goto error;
}
/* Beep should be sounding now so print synchronized BEEP. */
printf("BEEP");
fflush(stdout);
printf(" at %d, delta = %d\n",
(long) DATA.beepTime, (long) (DATA.beepTime - previousTime) );
fflush(stdout);
previousTime = DATA.beepTime;
}
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,172 @@
/*
* $Id$
* Play more sine waves than we can handle in real time as a stress test,
*
* Authors:
* Ross Bencina <rossb@audiomulch.com>
* Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define MAX_SINES (500)
#define MAX_LOAD (1.2)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (512)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TWOPI (M_PI * 2.0)
typedef struct paTestData
{
int numSines;
double phases[MAX_SINES];
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int j;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
float output = 0.0;
double phaseInc = 0.02;
double phase;
for( j=0; j<data->numSines; j++ )
{
/* Advance phase of next oscillator. */
phase = data->phases[j];
phase += phaseInc;
if( phase > TWOPI ) phase -= TWOPI;
phaseInc *= 1.02;
if( phaseInc > 0.5 ) phaseInc *= 0.5;
/* This is not a very efficient way to calc sines. */
output += (float) sin( phase );
data->phases[j] = phase;
}
*out++ = (float) (output / data->numSines);
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
int numStress;
paTestData data = {0};
double load;
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d. MAX_LOAD = %f\n",
SAMPLE_RATE, FRAMES_PER_BUFFER, MAX_LOAD );
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
1, /* mono output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
/* Determine number of sines required to get to 50% */
do
{
data.numSines++;
Pa_Sleep( 100 );
load = Pa_GetCPULoad( stream );
printf("numSines = %d, CPU load = %f\n", data.numSines, load );
}
while( load < 0.5 );
/* Calculate target stress value then ramp up to that level*/
numStress = (int) (2.0 * data.numSines * MAX_LOAD );
for( ; data.numSines < numStress; data.numSines++ )
{
Pa_Sleep( 200 );
load = Pa_GetCPULoad( stream );
printf("STRESSING: numSines = %d, CPU load = %f\n", data.numSines, load );
}
printf("Suffer for 5 seconds.\n");
Pa_Sleep( 5000 );
printf("Stop stream.\n");
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,151 @@
/*
* $Id$
* patest_underflow.c
* Simulate an output buffer underflow condition.
* Tests whether the stream can be stopped when underflowing buffers.
*
* Authors:
* Ross Bencina <rossb@audiomulch.com>
* Phil Burk <philburk@softsynth.com>
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
#define NUM_SECONDS (20)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (2048)
#define MSEC_PER_BUFFER ( (FRAMES_PER_BUFFER * 1000) / SAMPLE_RATE )
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
int sleepTime;
}
paTestData;
/* This routine will be called by the PortAudio engine when audio is needed.
** It may called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
*out++ = data->sine[data->right_phase]; /* right */
data->left_phase += 1;
if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE;
data->right_phase += 3; /* higher pitch so we can distinguish left and right. */
if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE;
}
/* Cause underflow to occur. */
if( data->sleepTime > 0 ) Pa_Sleep( data->sleepTime );
data->sleepTime += 1;
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );
}
data.left_phase = data.right_phase = data.sleepTime = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
err = Pa_OpenStream(
&stream,
paNoDevice,/* default input device */
0, /* no input */
paFloat32, /* 32 bit floating point input */
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
while( data.sleepTime < (2 * MSEC_PER_BUFFER) )
{
printf("SleepTime = %d\n", data.sleepTime );
Pa_Sleep( data.sleepTime );
}
printf("Try to stop stream.\n");
err = Pa_StopStream( stream ); /* */
err = Pa_AbortStream( stream ); /* */
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return err;
}

View file

@ -0,0 +1,144 @@
/*
* $Id$
* patest_wire.c
*
* Pass input directly to output.
* Note that some HW devices, for example many ISA audio cards
* on PCs, do NOT support full duplex! For a PC, you normally need
* a PCI based audio card such as the SBLive.
*
* Author: Phil Burk http://www.softsynth.com
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com
* Copyright (c) 1999-2000 Ross Bencina and 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 <math.h>
#include "portaudio.h"
/*
** Note that many of the older ISA sound cards on PCs do NOT support
** full duplex audio (simultaneous record and playback).
** And some only support full duplex at lower sample rates.
*/
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (64)
#if 1
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#else
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#endif
static int wireCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
/* This routine will be called by the PortAudio engine when audio is needed.
** It may be called at interrupt level on some machines so don't do anything
** that could mess up the system like calling malloc() or free().
*/
static int wireCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
{
SAMPLE *out = (SAMPLE*)outputBuffer;
SAMPLE *in = (SAMPLE*)inputBuffer;
unsigned int i;
(void) outTime;
/* This may get called with NULL inputBuffer during initial setup. */
if( inputBuffer == NULL )
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = 0; /* left */
*out++ = 0; /* right */
}
}
else
{
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = *in++; /* left */
*out++ = *in++; /* right */
}
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
err = Pa_Initialize();
if( err != paNoError ) goto error;
printf("PortAudio Test: input device ID = %d\n", Pa_GetDefaultInputDeviceID() );
printf("PortAudio Test: output device ID = %d\n", Pa_GetDefaultOutputDeviceID() );
err = Pa_OpenStream(
&stream,
Pa_GetDefaultInputDeviceID(), /* default output device */
2, /* stereo input */
PA_SAMPLE_TYPE,
NULL,
Pa_GetDefaultOutputDeviceID(), /* default output device */
2, /* stereo output */
PA_SAMPLE_TYPE,
NULL,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
wireCallback,
NULL ); /* no data */
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Full duplex sound test in progress.\n");
printf("Hit ENTER to exit test.\n"); fflush(stdout);
getchar();
printf("Closing stream.\n");
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
printf("Full duplex sound test complete.\n"); fflush(stdout);
return 0;
error:
Pa_Terminate();
fprintf( stderr, "An error occured while using the portaudio stream\n" );
fprintf( stderr, "Error number: %d\n", err );
fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
return -1;
}

View file

@ -0,0 +1,44 @@
# Make PortAudio for Linux
LIBS = -lm -lpthread
CC = /usr/local/bin/gcc
CDEFINES = -I../pa_common
CFLAGS = -g -Wall
PASRC = ../pa_common/pa_lib.c pa_unix_oss.c
PAINC = ../pa_common/portaudio.h
# Tests that work.
#TESTC = $(PASRC) ../pa_tests/patest_sine.c
#TESTC = $(PASRC) ../pa_tests/patest_longsine.c
#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c
#TESTC = $(PASRC) ../pa_tests/patest_maxsines.c
TESTC = $(PASRC) ../pa_tests/patest_toomanysines.c
#TESTC = $(PASRC) ../pa_tests/patest_underflow.c
#TESTC = $(PASRC) ../pa_tests/patest_hang.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/pa_fuzz.c
#TESTC = $(PASRC) ../pa_tests/patest_wire.c
#TESTC = $(PASRC) ../pa_tests/paqa_devs.c
# Tests that do not yet work.
# OSS doesn't let us make obscenely huge buffers so the test will seem to fail. But its OK.
#TESTC = $(PASRC) ../pa_tests/patest_stop.c
TESTH = $(PAINC)
all: patest
patest: $(TESTC) $(TESTH) Makefile
gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest
run: patest
./patest

Some files were not shown because too many files have changed in this diff Show more