tagging before directory restructure

This commit is contained in:
rossb 2006-05-15 03:58:09 +00:00
commit 61f17cde17
144 changed files with 46285 additions and 12650 deletions

194
Makefile.darwin Normal file
View file

@ -0,0 +1,194 @@
#
# PortAudio V19 Makefile.darwin
#
# Bjorn Roche of XO Audio (www.xoaudio.com)
# Based on autoconf's makefile, created from Makefile.in by Dominic Mazzoni
#
PREFIX = /usr/local
CC = gcc
CFLAGS = -g -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3 -O2 -Wall -pedantic -pipe -fPIC -DPA_BIG_ENDIAN -Ipa_common -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DPA_USE_COREAUDIO=1
LIBS = -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon
AR = /usr/bin/ar
RANLIB = ranlib
INSTALL = /usr/bin/install -c
SHARED_FLAGS = -framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon -dynamiclib -arch i386 -arch ppc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -mmacosx-version-min=10.3
DLL_LIBS =
CXXFLAGS =
NASM =
NASMOPT =
OTHER_OBJS = pa_mac/pa_mac_hostapis.o pa_unix/pa_unix_util.o pa_mac_core/pa_mac_core.o
PALIB = libportaudio.a
PADLL = libportaudio
PADLLV_MIN = 19
PADLLV = 0.0.19
PADLLEXT = .dylib
PASHLIBNAME = $(PADLL).$(PADLLV)$(PADLLEXT)
PAINC = pa_common/portaudio.h
COMMON_OBJS = \
pa_common/pa_allocation.o \
pa_common/pa_converters.o \
pa_common/pa_cpuload.o \
pa_common/pa_dither.o \
pa_common/pa_front.o \
pa_common/pa_process.o \
pa_common/pa_skeleton.o \
pa_common/pa_stream.o \
pa_common/pa_trace.o
TESTS = \
bin/paqa_devs \
bin/paqa_errs \
bin/patest1 \
bin/patest_buffer \
bin/patest_callbackstop \
bin/patest_clip \
bin/patest_dither \
bin/patest_hang \
bin/patest_in_overflow \
bin/patest_latency \
bin/patest_leftright \
bin/patest_longsine \
bin/patest_many \
bin/patest_maxsines \
bin/patest_multi_sine \
bin/patest_out_underflow \
bin/patest_pink \
bin/patest_prime \
bin/patest_read_record \
bin/patest_read_write_wire \
bin/patest_record \
bin/patest_ringmix \
bin/patest_saw \
bin/patest_sine8 \
bin/patest_sine \
bin/patest_sine_formats \
bin/patest_sine_time \
bin/patest_start_stop \
bin/patest_stop \
bin/patest_sync \
bin/patest_toomanysines \
bin/patest_underflow \
bin/patest_wire \
bin/patest_write_sine \
bin/pa_devs \
bin/pa_fuzz \
bin/pa_minlat
# Most of these don't compile yet. Put them in TESTS, above, if
# you want to try to compile them...
ALL_TESTS = \
bin/debug_convert \
bin/debug_dither_calc \
bin/debug_dual \
bin/debug_multi_in \
bin/debug_multi_out \
bin/debug_record \
bin/debug_record_reuse \
bin/debug_sine_amp \
bin/debug_sine \
bin/debug_sine_formats \
bin/debug_srate \
bin/debug_test1 \
bin/pa_devs \
bin/pa_fuzz \
bin/pa_minlat \
bin/paqa_devs \
bin/paqa_errs \
bin/patest1 \
bin/patest_buffer \
bin/patest_clip \
bin/patest_dither \
bin/patest_hang \
bin/patest_in_overflow \
bin/patest_latency \
bin/patest_leftright \
bin/patest_longsine \
bin/patest_many \
bin/patest_maxsines \
bin/patest_multi_sine \
bin/patest_out_underflow \
bin/patest_pink \
bin/patest_read_record \
bin/patest_record \
bin/patest_ringmix \
bin/patest_saw \
bin/patest_sine8 \
bin/patest_sine \
bin/patest_sine_formats \
bin/patest_sine_time \
bin/patest_start_stop \
bin/patest_stop \
bin/patest_sync \
bin/patest_toomanysines \
bin/patest_underflow \
bin/patest_wire \
bin/patest_write_sine
OBJS = $(COMMON_OBJS) $(OTHER_OBJS)
all: lib/$(PALIB) lib/$(PASHLIBNAME) tests
tests: bin/ $(TESTS)
lib/$(PALIB): lib/ $(OBJS) Makefile.darwin $(PAINC)
-rm lib/$(PALIB)
$(AR) ruv lib/$(PALIB) $(OBJS)
$(RANLIB) lib/$(PALIB)
lib/$(PASHLIBNAME): lib/ $(OBJS) Makefile.darwin $(PAINC)
$(CC) $(SHARED_FLAGS) -o lib/$(PASHLIBNAME) $(OBJS) $(DLL_LIBS) -install_name $(PREFIX)/lib/$(PASHLIBNAME) -compatibility_version $(PADLLV) -current_version $(PADLLV)
$(TESTS): bin/%: lib/$(PALIB) Makefile.darwin $(PAINC) pa_tests/%.c
$(CC) -o $@ $(CFLAGS) pa_tests/$*.c lib/$(PALIB) $(LIBS)
install: lib/$(PALIB) lib/$(PASHLIBNAME) portaudio-2.0.pc
$(INSTALL) -d $(PREFIX)/lib
$(INSTALL) -m 644 lib/$(PASHLIBNAME) $(PREFIX)/lib/$(PASHLIBNAME)
$(INSTALL) -m 644 lib/$(PALIB) $(PREFIX)/lib/$(PALIB)
cd $(PREFIX)/lib && rm -f $(PADLL)$(PADLLEXT) && ln -s $(PASHLIBNAME) $(PADLL)$(PADLLEXT)
$(INSTALL) -d $(PREFIX)/include
$(INSTALL) -m 644 pa_common/portaudio.h $(PREFIX)/include/portaudio.h
$(INSTALL) -d $(PREFIX)/lib/pkgconfig
$(INSTALL) -m 644 portaudio-2.0.pc $(PREFIX)/lib/pkgconfig/portaudio-2.0.pc
@echo ""
@echo "------------------------------------------------------------"
@echo "PortAudio was successfully installed."
@echo ""
@echo "On some systems (e.g. Linux) you should run 'ldconfig' now"
@echo "to make the shared object available. You may also need to"
@echo "modify your LD_LIBRARY_PATH environment variable to include"
@echo "the directory $(PREFIX)/lib"
@echo "------------------------------------------------------------"
@echo ""
uninstall:
rm -f $(PREFIX)/lib/$(PASHLIBNAME)
rm -f $(PREFIX)/lib/$(PALIB)
rm -f $(PREFIX)/lib/$(PADLL)$(PADLLEXT)
rm -f $(PREFIX)/include/portaudio.h
clean:
rm -f $(OBJS) $(TESTS) lib/$(PALIB) lib/$(PASHLIBNAME)
%.o: %.c Makefile.darwin $(PAINC)
$(CC) -c $(CFLAGS) $< -o $@
%.o: %.cpp Makefile.darwin $(PAINC)
$(CXX) -c $(CXXFLAGS) $< -o $@
%.o: %.asm
$(NASM) $(NASMOPT) -o $@ $<
bin:
mkdir bin
lib:
mkdir lib

View file

@ -1,42 +1,61 @@
#
# PortAudio Makefile.in
# PortAudio V19 Makefile.in
#
# Dominic Mazzoni
#
PREFIX = @prefix@
CC = @CC@
CFLAGS = @CFLAGS@ -Ipa_common
CFLAGS = @CFLAGS@ -Ipa_common @DEFS@
LIBS = @LIBS@
AR = @AR@
RANLIB = @RANLIB@
INSTALL = @INSTALL@
SHARED_FLAGS = @SHARED_FLAGS@
DLL_LIBS = @DLL_LIBS@
CXXFLAGS = @CXXFLAGS@
NASM = @NASM@
NASMOPT = @NASMOPT@
OTHER_OBJS = @OTHER_OBJS@
PALIB = libportaudio.a
PADLL = @PADLL@
PADLLV = $(PADLL).0.0.18
PADLLV = $(PADLL).0.0.19
PAINC = pa_common/portaudio.h
COMMON_OBJS = \
pa_common/pa_convert.o \
pa_common/pa_lib.o
pa_common/pa_allocation.o \
pa_common/pa_converters.o \
pa_common/pa_cpuload.o \
pa_common/pa_dither.o \
pa_common/pa_front.o \
pa_common/pa_process.o \
pa_common/pa_skeleton.o \
pa_common/pa_stream.o \
pa_common/pa_trace.o
TESTS = \
bin/paqa_devs \
bin/paqa_errs \
bin/patest1 \
bin/patest_buffer \
bin/patest_callbackstop \
bin/patest_clip \
bin/patest_dither \
bin/patest_hang \
bin/patest_in_overflow \
bin/patest_latency \
bin/patest_leftright \
bin/patest_longsine \
bin/patest_many \
bin/patest_maxsines \
bin/patest_multi_sine \
bin/patest_out_underflow \
bin/patest_pink \
bin/patest_prime \
bin/patest_read_record \
bin/patest_read_write_wire \
bin/patest_record \
bin/patest_ringmix \
bin/patest_saw \
@ -44,11 +63,66 @@ TESTS = \
bin/patest_sine \
bin/patest_sine_formats \
bin/patest_sine_time \
bin/patest_start_stop \
bin/patest_stop \
bin/patest_sync \
bin/patest_toomanysines \
bin/patest_underflow \
bin/patest_wire
bin/patest_wire \
bin/patest_write_sine \
bin/pa_devs \
bin/pa_fuzz \
bin/pa_minlat
# Most of these don't compile yet. Put them in TESTS, above, if
# you want to try to compile them...
ALL_TESTS = \
bin/debug_convert \
bin/debug_dither_calc \
bin/debug_dual \
bin/debug_multi_in \
bin/debug_multi_out \
bin/debug_record \
bin/debug_record_reuse \
bin/debug_sine_amp \
bin/debug_sine \
bin/debug_sine_formats \
bin/debug_srate \
bin/debug_test1 \
bin/pa_devs \
bin/pa_fuzz \
bin/pa_minlat \
bin/paqa_devs \
bin/paqa_errs \
bin/patest1 \
bin/patest_buffer \
bin/patest_clip \
bin/patest_dither \
bin/patest_hang \
bin/patest_in_overflow \
bin/patest_latency \
bin/patest_leftright \
bin/patest_longsine \
bin/patest_many \
bin/patest_maxsines \
bin/patest_multi_sine \
bin/patest_out_underflow \
bin/patest_pink \
bin/patest_read_record \
bin/patest_record \
bin/patest_ringmix \
bin/patest_saw \
bin/patest_sine8 \
bin/patest_sine \
bin/patest_sine_formats \
bin/patest_sine_time \
bin/patest_start_stop \
bin/patest_stop \
bin/patest_sync \
bin/patest_toomanysines \
bin/patest_underflow \
bin/patest_wire \
bin/patest_write_sine
OBJS = $(COMMON_OBJS) $(OTHER_OBJS)
@ -66,11 +140,15 @@ lib/$(PADLLV): lib/ $(OBJS) Makefile $(PAINC)
$(TESTS): bin/%: lib/$(PALIB) Makefile $(PAINC) pa_tests/%.c
$(CC) -o $@ $(CFLAGS) pa_tests/$*.c lib/$(PALIB) $(LIBS)
install: lib/$(PALIB) lib/$(PADLLV)
install: lib/$(PALIB) lib/$(PADLLV) portaudio-2.0.pc
$(INSTALL) -d $(PREFIX)/lib
$(INSTALL) -m 644 lib/$(PADLLV) $(PREFIX)/lib/$(PADLLV)
$(INSTALL) -m 644 lib/$(PALIB) $(PREFIX)/lib/$(PALIB)
cd $(PREFIX)/lib && rm -f $(PADLL) && ln -s $(PADLLV) $(PADLL)
$(INSTALL) -d $(PREFIX)/include
$(INSTALL) -m 644 pa_common/portaudio.h $(PREFIX)/include/portaudio.h
$(INSTALL) -d $(PREFIX)/lib/pkgconfig
$(INSTALL) -m 644 portaudio-2.0.pc $(PREFIX)/lib/pkgconfig/portaudio-2.0.pc
@echo ""
@echo "------------------------------------------------------------"
@echo "PortAudio was successfully installed."
@ -82,14 +160,29 @@ install: lib/$(PALIB) lib/$(PADLLV)
@echo "------------------------------------------------------------"
@echo ""
uninstall:
rm -f $(PREFIX)/lib/$(PADLLV)
rm -f $(PREFIX)/lib/$(PALIB)
rm -f $(PREFIX)/lib/$(PADLL)
rm -f $(PREFIX)/include/portaudio.h
clean:
rm -f $(OBJS) $(TESTS) lib/$(PALIB)
rm -f $(OBJS) $(TESTS) lib/$(PALIB) lib/$(PADLLV)
%.o: %.c Makefile $(PAINC)
$(CC) -c $(CFLAGS) $< -o $@
%.o: %.cpp Makefile $(PAINC)
$(CXX) -c $(CXXFLAGS) $< -o $@
%.o: %.asm
$(NASM) $(NASMOPT) -o $@ $<
bin:
mkdir bin
lib:
mkdir lib

View file

@ -1,59 +0,0 @@
# 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
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

@ -1,57 +0,0 @@
# 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

@ -58,7 +58,7 @@ Important Files and Folders:
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_sm = 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

268
SConscript Normal file
View file

@ -0,0 +1,268 @@
import os.path, copy, sys
# Inspired by the versioning scheme followed by Qt, it seems sensible enough. There are three components: major, minor
# and micro. Major changes with each subtraction from the API (backward-incompatible, i.e. V19 vs. V18), minor changes
# with each addition to the API (backward-compatible), micro changes with each revision of the source code.
ApiVer = "2.0.0"
def checkSymbol(conf, header, library=None, symbol=None, autoAdd=True, critical=False, pkgName=None):
""" Check for symbol in library, optionally look only for header.
@param conf: Configure instance.
@param header: The header file where the symbol is declared.
@param library: The library in which the symbol exists, if None it is taken to be the standard C library.
@param symbol: The symbol to look for, if None only the header will be looked up.
@param autoAdd: Automatically link with this library if check is positive.
@param critical: Raise on error?
@param pkgName: Optional name of pkg-config entry for library, to determine build parameters.
@return: True/False
"""
origEnv = conf.env.Copy() # Copy unmodified environment so we can restore it upon error
env = conf.env
if library is None:
library = "c" # Standard library
autoAdd = False
if pkgName is not None:
origLibs = copy.copy(env.get("LIBS", None))
try: env.ParseConfig("pkg-config --silence-errors %s --cflags --libs" % pkgName)
except: pass
else:
# I see no other way of checking that the parsing succeeded, if it did add no more linking parameters
if env.get("LIBS", None) != origLibs:
autoAdd = False
try:
if not conf.CheckCHeader(header, include_quotes="<>"):
raise ConfigurationError("missing header %s" % header)
if symbol is not None and not conf.CheckLib(library, symbol, language="C", autoadd=autoAdd):
raise ConfigurationError("missing symbol %s in library %s" % (symbol, library))
except ConfigurationError:
conf.env = origEnv
if not critical:
return False
raise
return True
import SCons.Errors
# Import common variables
try:
Import("Platform", "Posix", "ConfigurationError")
except SCons.Errors.UserError:
# The common objects must be exported first
SConscript(os.path.join("scons", "SConscript_common"))
Import("Platform", "Posix", "ConfigurationError")
Import("env")
# This will be manipulated
env = env.Copy()
env.Append(CPPPATH="pa_common")
# Store all signatures in one file, otherwise .sconsign files will get installed along with our own files
env.SConsignFile(os.path.join("scons", ".sconsign"))
# We operate with a set of needed libraries and optional libraries, the latter stemming from host API implementations.
# For libraries of both types we record a set of values that is used to look for the library in question, during
# configuration. If the corresponding library for a host API implementation isn't found, the implementation is left out.
neededLibs = []
optionalImpls = {}
if Platform in Posix:
neededLibs += [("pthread", "pthread.h", "pthread_create"), ("m", "math.h", "sin")]
if env["useALSA"]:
optionalImpls["ALSA"] = ("asound", "alsa/asoundlib.h", "snd_pcm_open")
if env["useJACK"]:
optionalImpls["JACK"] = ("jack", "jack/jack.h", "jack_client_new")
if env["useOSS"]:
# TODO: It looks like the prefix for soundcard.h depends on the platform
optionalImpls["OSS"] = ("oss", "sys/soundcard.h", None)
else:
raise ConfigurationError("unknown platform %s" % Platform)
if Platform == "darwin":
env.Append(LINKFLAGS=["-framework CoreAudio", "-framework AudioToolBox"])
env.Append(CPPDEFINES=["PA_USE_COREAUDIO"])
elif Platform == "cygwin":
env.Append(LIBS=["winmm"])
elif Platform == "irix":
neededLibs += [("audio", "dmedia/audio.h", "alOpenPort"), ("dmedia", "dmedia/dmedia.h", "dmGetUST")]
env.Append(CPPDEFINES=["PA_USE_SGI"])
def CheckCTypeSize(context, tp):
""" Check size of C type.
@param context: A configuration context.
@param tp: The type to check.
@return: Size of type, in bytes.
"""
context.Message("Checking the size of C type %s..." % tp)
ret = context.TryRun("""
#include <stdio.h>
int main() {
printf("%%d", sizeof(%s));
return 0;
}
""" % tp, ".c")
if not ret[0]:
print "Hihi"
context.Result(" Couldn't obtain size of type %s!" % tp)
return None
assert ret[1]
sz = int(ret[1])
context.Result("%d" % sz)
return sz
if sys.byteorder == "little":
env.Append(CPPDEFINES=["PA_LITTLE_ENDIAN"])
elif sys.byteorder == "big":
env.Append(CPPDEFINES=["PA_BIG_ENDIAN"])
else:
raise ConfigurationError("unknown byte order: %s" % sys.byteorder)
if env["enableDebugOutput"]:
env.Append(CPPDEFINES=["PA_ENABLE_DEBUG_OUTPUT"])
# Start configuration
# Use an absolute path for conf_dir, otherwise it gets created both relative to current directory and build directory
conf = env.Configure(log_file=os.path.join("scons", "sconf.log"), custom_tests={"CheckCTypeSize": CheckCTypeSize},
conf_dir=os.path.join(os.getcwd(), "scons", ".sconf_temp"))
assert os.path.exists(os.path.join("scons", ".sconf_temp"))
env.Append(CPPDEFINES=["SIZEOF_SHORT=%d" % conf.CheckCTypeSize("short")])
env.Append(CPPDEFINES=["SIZEOF_INT=%d" % conf.CheckCTypeSize("int")])
env.Append(CPPDEFINES=["SIZEOF_LONG=%d" % conf.CheckCTypeSize("long")])
if checkSymbol(conf, "time.h", "rt", "clock_gettime"):
env.Append(CPPDEFINES=["HAVE_CLOCK_GETTIME"])
if checkSymbol(conf, "time.h", symbol="nanosleep"):
env.Append(CPPDEFINES=["HAVE_NANOSLEEP"])
# Look for needed libraries and link with them
for lib, hdr, sym in neededLibs:
checkSymbol(conf, hdr, lib, sym, critical=True)
# Look for host API libraries, if a library isn't found disable corresponding host API implementation.
for name, val in optionalImpls.items():
lib, hdr, sym = val
if checkSymbol(conf, hdr, lib, sym, critical=False, pkgName=name.lower()):
env.Append(CPPDEFINES=["PA_USE_%s=1" % name.upper()])
else:
del optionalImpls[name]
# Configuration finished
env = conf.Finish()
# PA infrastructure
CommonSources = [os.path.join("pa_common", f) for f in "pa_allocation.c pa_converters.c pa_cpuload.c pa_dither.c pa_front.c \
pa_process.c pa_skeleton.c pa_stream.c pa_trace.c".split()]
# Host API implementations
ImplSources = []
if Platform in Posix:
ThreadCFlags = "-pthread"
env.AppendUnique(LINKFLAGS=[ThreadCFlags])
BaseCFlags = "-Wall -pedantic -pipe %s" % ThreadCFlags
DebugCFlags = "-g"
OptCFlags = "-O2"
ImplSources += [os.path.join("pa_unix", f) for f in "pa_unix_hostapis.c pa_unix_util.c".split()]
if "ALSA" in optionalImpls:
ImplSources.append(os.path.join("pa_linux_alsa", "pa_linux_alsa.c"))
if "JACK" in optionalImpls:
ImplSources.append(os.path.join("pa_jack", "pa_jack.c"))
if "OSS" in optionalImpls:
ImplSources.append(os.path.join("pa_unix_oss", "pa_unix_oss.c"))
env["CCFLAGS"] = BaseCFlags
if env["enableDebug"]:
env["CCFLAGS"] += " " + DebugCFlags
if env["enableOptimize"]:
env["CCFLAGS"] += " " + OptCFlags
if not env["enableAsserts"]:
env.Append(CPPDEFINES=["-DNDEBUG"])
if env["customCFlags"]:
env["CCFLAGS"] += " " + env["customCFlags"]
if env["customLinkFlags"]:
env["LINKFLAGS"] += " " + env["customLinkFlags"]
sources = CommonSources + ImplSources
if env["enableShared"]:
sharedLibEnv = env.Copy()
if Platform in Posix:
# Add soname to library, this is so a reference is made to the versioned library in programs linking against libportaudio.so
sharedLibEnv.AppendUnique(SHLINKFLAGS="-Wl,-soname=libportaudio.so.%d" % int(ApiVer.split(".")[0]))
sharedLib = sharedLibEnv.SharedLibrary(target="portaudio", source=sources)
else:
sharedLib = None
staticLib = env.StaticLibrary(target="portaudio", source=sources)
if Platform in Posix:
prefix = env["prefix"]
includeDir = os.path.join(prefix, "include")
env.Alias("install", env.Install(includeDir, os.path.join("pa_common", "portaudio.h")))
libDir = os.path.join(prefix, "lib")
if env["enableStatic"]:
env.Alias("install", env.Install(libDir, staticLib))
def symlink(env, target, source):
trgt = str(target[0])
src = str(source[0])
if os.path.lexists(trgt):
os.remove(trgt)
os.symlink(os.path.basename(src), trgt)
if env["enableShared"]:
major, minor, micro = [int(c) for c in ApiVer.split(".")]
soFile = "%s.%s" % (sharedLib[0], ApiVer)
env.Alias("install", env.InstallAs(target=os.path.join(libDir, soFile), source=sharedLib))
# Install symlinks
symTrgt = os.path.join(libDir, soFile)
env.Alias("install", env.Command(os.path.join(libDir, "libportaudio.so.%d.%d" % (major, minor)),
symTrgt, symlink))
symTrgt = symTrgt.rsplit(".", 1)[0]
env.Alias("install", env.Command(os.path.join(libDir, "libportaudio.so.%d" % major), symTrgt, symlink))
symTrgt = symTrgt.rsplit(".", 1)[0]
env.Alias("install", env.Command(os.path.join(libDir, "libportaudio.so"), symTrgt, symlink))
# pkg-config
def installPkgconfig(env, target, source):
tgt = str(target[0])
src = str(source[0])
f = open(src)
try: txt = f.read()
finally: f.close()
txt = txt.replace("@prefix@", prefix)
txt = txt.replace("@libdir@", libDir)
txt = txt.replace("@includedir@", includeDir)
txt = txt.replace("@LIBS@", " ".join(["-l%s" % l for l in env["LIBS"]]))
txt = txt.replace("@THREAD_CFLAGS@", ThreadCFlags)
f = open(tgt, "w")
try: f.write(txt)
finally: f.close()
pkgconfigTgt = "portaudio-%d.0.pc" % int(ApiVer.split(".", 1)[0])
env.Alias("install", env.Command(os.path.join(libDir, "pkgconfig", pkgconfigTgt),
pkgconfigTgt + ".in", installPkgconfig))
testNames = ["patest_sine", "paqa_devs", "paqa_errs", "patest1", "patest_buffer", "patest_callbackstop", "patest_clip", \
"patest_dither", "patest_hang", "patest_in_overflow", "patest_latency", "patest_leftright", "patest_longsine", \
"patest_many", "patest_maxsines", "patest_multi_sine", "patest_out_underflow", "patest_pink", "patest_prime", \
"patest_read_record", "patest_record", "patest_ringmix", "patest_saw", "patest_sine8", "patest_sine", \
"patest_sine_time", "patest_start_stop", "patest_stop", "patest_sync", \
"patest_toomanysines", "patest_underflow", "patest_wire", "patest_write_sine", "pa_devs", "pa_fuzz", "pa_minlat"]
# The test directory ("bin") should be in the top-level PA directory, the calling script should ensure that SCons doesn't
# switch current directory by calling SConscriptChdir(False). Specifying an absolute directory for each target means it won't
# be relative to the build directory
tests = [env.Program(target=os.path.join(os.getcwd(), "bin", name), source=[os.path.join("pa_tests", name + ".c"),
staticLib]) for name in testNames]
Return("sources", "sharedLib", "staticLib", "tests", "env")

31
SConstruct Normal file
View file

@ -0,0 +1,31 @@
import sys, os.path
# SConscript_opts exports PortAudio options
optsDict = SConscript(os.path.join("scons", "SConscript_opts"))
optionsCache = os.path.join("scons", "options.cache") # Save options between runs in this cache
options = Options(optionsCache, args=ARGUMENTS)
for k in ("Installation Dirs", "Library Types", "Host APIs", "Build Parameters"):
options.AddOptions(*optsDict[k])
# Propagate options into environment
env = Environment(options=options)
# Save options for next run
options.Save(optionsCache, env)
# Generate help text for options
env.Help(options.GenerateHelpText(env))
buildDir = os.path.join("#", "scons", "build")
# Don't change directory when running the SConscript
env.SConscriptChdir(False)
sources, sharedLib, staticLib, tests, portEnv = env.SConscript("SConscript", build_dir=buildDir, duplicate=False, exports=["env"])
# Build these by default
env.Default(sharedLib, staticLib, tests)
"""
if env["enablePython"]:
env.SConscriptChdir(True)
pyEnv = env.Copy()
pyEnv["CCFLAGS"] = portEnv["CCFLAGS"].replace("-pedantic", "")
module = env.SConscript("python/SConscript", exports={"env": pyEnv, "BuildDir": BuildDir})
env.Default(module)
"""

222
V19-devel-readme.txt Normal file
View file

@ -0,0 +1,222 @@
STATUS:
MME, DirectSound and ASIO versions are more-or-less working. See FIXMEs @todos
and the proposals matrix at portaudio.com for further status.
The pa_tests directory contains tests. pa_tests/README.txt notes which tests
currently build.
The PaUtil support code is finished enough for other implementations to be
ported. No changes are expected to be made to the definition of the PaUtil
functions.
Note that it's not yet 100% clear how the current support functions
will interact with blocking read/write streams.
BUILD INSTRUCTIONS
to build tests/patest_sine.c you will need to compile and link the following
files (MME)
pa_common\pa_process.c
pa_common\pa_skeleton.c
pa_common\pa_stream.c
pa_common\pa_trace.c
pa_common\pa_converters.c
pa_common\pa_cpuload.c
pa_common\pa_dither.c
pa_common\pa_front.c
pa_common\pa_allocation.h
pa_win\pa_win_util.c
pa_win\pa_win_hostapis.c
pa_win_wmme\pa_win_wmme.c
see below for a description of these files.
FILES:
portaudio.h
public api header file
pa_front.c
implements the interface defined in portaudio.h. manages multiple host apis.
validates function parameters before calling through to host apis. tracks
open streams and closes them at Pa_Terminate().
pa_util.h
declares utility functions for use my implementations. including utility
functions which must be implemented separately for each platform.
pa_hostapi.h
hostapi representation structure used to interface between pa_front.c
and implementations
pa_stream.c/h
stream interface and representation structures and helper functions
used to interface between pa_front.c and implementations
pa_cpuload.c/h
source and header for cpu load calculation facility
pa_trace.c/h
source and header for debug trace log facility
pa_converters.c/h
sample buffer conversion facility
pa_dither.c/h
dither noise generator
pa_process.c/h
callback buffer processing facility including interleave and block adaption
pa_allocation.c/h
allocation context for tracking groups of allocations
pa_skeleton.c
an skeleton implementation showing how the common code can be used.
pa_win_util.c
Win32 implementation of platform specific PaUtil functions (memory allocation,
usec clock, Pa_Sleep().) The file will be used with all Win32 host APIs.
pa_win_hostapis.c
contains the paHostApiInitializers array and an implementation of
Pa_GetDefaultHostApi() for win32 builds.
pa_win_wmme.c
Win32 host api implementation for the windows multimedia extensions audio API.
pa_win_wmme.h
public header file containing interfaces to mme-specific functions and the
deviceInfo data structure.
CODING GUIDELINES:
naming conventions:
#defines begin with PA_
#defines local to a file end with _
global utility variables begin with paUtil
global utility types begin with PaUtil (including function types)
global utility functions begin with PaUtil_
static variables end with _
static constants begin with const and end with _
static funtions have no special prefix/suffix
In general, implementations should declare all of their members static,
except for their initializer which should be exported. All exported names
should be preceeded by Pa<MN>_ where MN is the module name, for example
the windows mme initializer should be named PaWinWmme_Initialize().
Every host api should define an initializer which returns an error code
and a PaHostApiInterface*. The initializer should only return an error other
than paNoError if it encounters an unexpected and fatal error (memory allocation
error for example). In general, there may be conditions under which it returns
a NULL interface pointer and also returns paNoError. For example, if the ASIO
implementation detects that ASIO is not installed, it should return a
NULL interface, and paNoError.
Platform-specific shared functions should begin with Pa<PN>_ where PN is the
platform name. eg. PaWin_ for windows, PaUnix_ for unix.
The above two conventions should also be followed whenever it is necessary to
share functions accross multiple source files.
Two utilities for debug messages are provided. The PA_DEBUG macro defined in
pa_implementation.h provides a simple way to print debug messages to stderr.
Due to real-time performance issues, PA_DEBUG may not be suitable for use
within the portaudio processing callback, or in other threads. In such cases
the event tracing facility provided in pa_trace.h may be more appropriate.
If PA_LOG_API_CALLS is defined, all calls to the public PortAudio API
will be logged to stderr along with parameter and return values.
TODO:
(this list is totally out of date)
finish coding converter functions in pa_converters.c (anyone?)
implement block adaption in pa_process.c (phil?)
fix all current tests to work with new code. this should mostly involve
changing PortAudioStream to PaStream, and GetDefaultDeviceID to GetDefaultDevice etc.
write some new tests to exercise the multi-api functions
write (doxygen) documentation for pa_trace (phil?)
remove unused typeids from PaHostAPITypeID
create a global configuration file which documents which PA_ defines can be
used for configuration
need a coding standard for comment formatting
migrate directx (phil)
migrate asio (ross?, stephane?)
see top of pa_win_wmme.c for MME todo items (ross)
write style guide document (ross)
DESIGN ISSUES:
(this list is totally out of date)
consider removing Pa_ConvertHostApiDeviceIndexToGlobalDeviceIndex() from the API
switch to new latency parameter mechanism now (?)
question: if input or outputDriverInfo structures are passed for a different
hostApi from the one being called, do we return an error or just ignore
them? (i think return error)
consider renaming PortAudioCallback to PaStreamCallback
consider renaming PaError, PaResult
ASSORTED DISORGANISED NOTES:
NOTE:
pa_lib.c performs the following validations for Pa_OpenStream() which we do not currently do:
- checks the device info to make sure that the device supports the requested sample rate,
it may also change the sample rate to the "closest available" sample rate if it
is within a particular error margin
rationale for breaking up internalPortAudioStream:
each implementation has its own requirements and behavior, and should be
able to choose the best way to operate without being limited by the
constraints imposed by a common infrastructure. in other words the
implementations should be able to pick and choose services from the
common infrastructure. currently identified services include:
- cpu load tracking
- buffering and conversion service (same code works for input and output)
- should support buffer multiplexing (non-integer length input and output buffers)
- in-place conversion where possible (only for callback, read/write always copies)
- should manage allocation of temporary buffers if necessary
- instrumentation (should be able to be disabled): callback count, framesProcessed
- common data: magic, streamInterface, callback, userdata
- conversion functions:
- should handle temp buffer allocation
- dithering (random number state per-stream)
- buffer size mismatches
- with new buffer slip rules, temp buffers may always be needed
- we should aim for in-place conversion wherever possible
- does phil's code support in-place conversion? (yes)
- dicuss relationship between user and host buffer sizes
- completely independent.. individual implementations may constrain
host buffer sizes if necessary
- discuss device capabilities:
- i'd like to be able to request certain information:
- channel count for example

57
aclocal.m4 vendored Normal file
View file

@ -0,0 +1,57 @@
dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not)
dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page
dnl also defines GSTUFF_PKG_ERRORS on error
AC_DEFUN(PKG_CHECK_MODULES, [
succeeded=no
if test -z "$PKG_CONFIG"; then
AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
fi
if test "$PKG_CONFIG" = "no" ; then
echo "*** The pkg-config script could not be found. Make sure it is"
echo "*** in your path, or set the PKG_CONFIG environment variable"
echo "*** to the full path to pkg-config."
echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config."
else
PKG_CONFIG_MIN_VERSION=0.9.0
if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then
AC_MSG_CHECKING(for $2)
if $PKG_CONFIG --exists "$2" ; then
AC_MSG_RESULT(yes)
succeeded=yes
AC_MSG_CHECKING($1_CFLAGS)
$1_CFLAGS=`$PKG_CONFIG --cflags "$2"`
AC_MSG_RESULT($$1_CFLAGS)
AC_MSG_CHECKING($1_LIBS)
$1_LIBS=`$PKG_CONFIG --libs "$2"`
AC_MSG_RESULT($$1_LIBS)
else
$1_CFLAGS=""
$1_LIBS=""
## If we have a custom action on failure, don't print errors, but
## do set a variable so people can do so.
$1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
ifelse([$4], ,echo $$1_PKG_ERRORS,)
fi
AC_SUBST($1_CFLAGS)
AC_SUBST($1_LIBS)
else
echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer."
echo "*** See http://www.freedesktop.org/software/pkgconfig"
fi
fi
if test $succeeded = yes; then
ifelse([$3], , :, [$3])
else
ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4])
fi
])

185
config.doxy Normal file
View file

@ -0,0 +1,185 @@
# Doxyfile 1.2.13-20020210
#---------------------------------------------------------------------------
# General configuration options
#---------------------------------------------------------------------------
PROJECT_NAME = PortAudio
PROJECT_NUMBER = 2.0
OUTPUT_DIRECTORY = "./docs/"
OUTPUT_LANGUAGE = English
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
INTERNAL_DOCS = NO
STRIP_CODE_COMMENTS = YES
CASE_SENSE_NAMES = YES
SHORT_NAMES = NO
HIDE_SCOPE_NAMES = NO
VERBATIM_HEADERS = YES
SHOW_INCLUDE_FILES = YES
JAVADOC_AUTOBRIEF = NO
DETAILS_AT_TOP = NO
INHERIT_DOCS = YES
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
DISTRIBUTE_GROUP_DOC = NO
TAB_SIZE = 8
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
ALIASES =
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = ./pa_common ./pa_win_wmme ./pa_asio ./pa_win_ds ./pa_linux_alsa ./pa_unix_oss ./pa_jack ./pa_tests
FILE_PATTERNS = *.h *.c *.cpp
RECURSIVE = YES
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = doxygen_html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_ALIGN_MEMBERS = YES
GENERATE_HTMLHELP = NO
GENERATE_CHI = NO
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = NO
ENUM_VALUES_PER_LINE = 4
GENERATE_TREEVIEW = NO
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4wide
EXTRA_PACKAGES =
LATEX_HEADER =
PDF_HYPERLINKS = NO
USE_PDFLATEX = NO
LATEX_BATCHMODE = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED =
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
HIDE_UNDOC_RELATIONS = NO
HAVE_DOT = NO
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
TEMPLATE_RELATIONS = YES
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
GRAPHICAL_HIERARCHY = YES
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_WIDTH = 1024
MAX_DOT_GRAPH_HEIGHT = 1024
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO
CGI_NAME = search.cgi
CGI_URL =
DOC_URL =
DOC_ABSPATH =
BIN_ABSPATH = /usr/local/bin/
EXT_DOC_PATHS =

BIN
config.sub vendored

Binary file not shown.

BIN
configure vendored

Binary file not shown.

View file

@ -1,7 +1,7 @@
dnl
dnl PortAudio configure.in script
dnl portaudio V19 configure.in script
dnl
dnl Dominic Mazzoni
dnl Dominic Mazzoni, Arve Knudsen
dnl
dnl Require autoconf >= 2.13
@ -11,43 +11,183 @@ dnl Init autoconf and make sure configure is being called
dnl from the right directory
AC_INIT([pa_common/portaudio.h])
dnl Checks for programs
dnl Specify options
AC_ARG_WITH(alsa,
[ --with-alsa (default=yes)],
with_alsa=$withval, with_alsa="yes")
AC_ARG_WITH(jack,
[ --with-jack (default=yes)],
with_jack=$withval, with_jack="yes")
AC_ARG_WITH(oss,
[ --with-oss (default=yes)],
with_oss=$withval, with_oss="yes")
AC_ARG_WITH(host_os,
[ --with-host_os (no default)],
host_os=$withval)
AC_ARG_WITH(winapi,
[ --with-winapi ((wmme/directx/asio) default=wmme)],
with_winapi=$withval, with_winapi="wmme")
dnl Mac API added for ASIO, can have other api's listed
AC_ARG_WITH(macapi,
[ --with-macapi ((asio/core/sm) default=core)],
with_macapi=$withval, with_macapi="core")
AC_ARG_WITH(asiodir,
[ --with-asiodir (default=/usr/local/asiosdk2)],
with_asiodir=$withval, with_asiodir="/usr/local/asiosdk2")
AC_ARG_WITH(dxdir,
[ --with-dxdir (default=/usr/local/dx7sdk)],
with_dxdir=$withval, with_dxdir="/usr/local/dx7sdk")
AC_ARG_ENABLE(debug-output,
[ --enable-debug-output],
[if test x$enableval != xno ; then
AC_DEFINE(PA_ENABLE_DEBUG_OUTPUT,,[Enable debugging messages])
fi
])
dnl Checks for programs.
AC_PROG_CC
AC_PROG_RANLIB
AC_PROG_INSTALL
AC_PATH_PROG(AR, ar, no)
if [[ $AR = "no" ]] ; then
AC_MSG_ERROR("Could not find ar - needed to create a library");
AC_MSG_ERROR("Could not find ar - needed to create a library");
fi
dnl Extra variables we want to substitute
dnl This must be one of the first tests we do or it will fail...
AC_C_BIGENDIAN
dnl checks for various host APIs and arguments to configure that
dnl turn them on or off
AC_CHECK_LIB(asound, snd_pcm_open, have_alsa=yes, have_alsa=no)
dnl Determine the host description for the subsequent test.
dnl PKG_CHECK_MODULES seems to check and set the host variable also, but
dnl that then requires pkg-config availability which is not standard on
dnl MinGW systems and can be a pain to install.
AC_CANONICAL_HOST
PKG_CHECK_MODULES(JACK, jack, have_jack=yes, have_jack=no)
dnl sizeof checks: we will need a 16-bit and a 32-bit type
AC_CHECK_SIZEOF(short)
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(long)
dnl extra variables
AC_SUBST(OTHER_OBJS)
AC_SUBST(PADLL)
AC_SUBST(SHARED_FLAGS)
AC_SUBST(THREAD_CFLAGS)
AC_SUBST(DLL_LIBS)
AC_SUBST(CXXFLAGS)
AC_SUBST(NASM)
AC_SUBST(NASMOPT)
dnl Determine the host operating system / platform
AC_CANONICAL_HOST
CFLAGS="-g -O2 -Wall -pedantic -pipe -fPIC"
if [[ $ac_cv_c_bigendian = "yes" ]] ; then
CFLAGS="$CFLAGS -DPA_BIG_ENDIAN"
else
CFLAGS="$CFLAGS -DPA_LITTLE_ENDIAN"
fi
case "${host_os}" in
darwin* )
dnl Mac OS X configuration
OTHER_OBJS="pa_mac_core/pa_mac_core.o";
LIBS="-framework CoreAudio -lm";
AC_DEFINE(PA_USE_COREAUDIO)
OTHER_OBJS="pa_mac/pa_mac_hostapis.o pa_unix/pa_unix_util.o pa_mac_core/pa_mac_core.o";
LIBS="-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon";
PADLL="libportaudio.dylib";
SHARED_FLAGS="-framework CoreAudio -dynamiclib";
SHARED_FLAGS="-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon -dynamiclib";
if [[ $with_macapi = "asio" ]] ; then
if [[ $with_asiodir ]] ; then
ASIODIR="$with_asiodir";
else
ASIODIR="/usr/local/asiosdk2";
fi
echo "ASIODIR: $ASIODIR";
OTHER_OBJS="$CFLAGS pa_asio/iasiothiscallresolver.o $ASIODIR/host/asiodrivers.o $ASIODIR/common/asio.o $ASIODIR/host/mac/asioshlib.o";
CFLAGS="$CFLAGS -Ipa_asio -I$ASIDIR/host/mac -I$ASIODIR/common";
CXXFLAGS="$CFLAGS";
fi
;;
mingw* )
dnl MingW configuration
dnl MingW configuration
OTHER_OBJS="pa_win_wmme/pa_win_wmme.o";
LIBS="-lwinmm -lm";
PADLL="portaudio.dll";
SHARED_FLAGS="-shared -mthreads";
DLL_LIBS="-lwinmm";
;;
echo "WINAPI: $with_winapi"
if [[ $with_winapi = "directx" ]] ; then
if [[ $with_dxdir ]] ; then
DXDIR="$with_dxdir";
else
DXDIR="/usr/local/dx7sdk";
fi
echo "DXDIR: $DXDIR"
OTHER_OBJS="pa_win_ds/pa_win_ds.o pa_win_ds/dsound_wrapper.o pa_win/pa_win_hostapis.o pa_win/pa_win_util.o";
LIBS="-lwinmm -lm -ldsound -lole32";
PADLL="portaudio.dll";
THREAD_CFLAGS="-mthreads"
SHARED_FLAGS="-shared";
DLL_LIBS="-lwinmm -lm -L./dx7sdk/lib -ldsound -lole32";
#VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\"";
#CFLAGS="$CFLAGS -I$VC98 -DPA_NO_WMME -DPA_NO_ASIO";
CFLAGS="$CFLAGS -Ipa_common -I$DXDIR/include -DPA_NO_WMME -DPA_NO_ASIO" -DPA_NO_WDMKS;
elif [[ $with_winapi = "asio" ]] ; then
if [[ $with_asiodir ]] ; then
ASIODIR="$with_asiodir";
else
ASIODIR="/usr/local/asiosdk2";
fi
echo "ASIODIR: $ASIODIR"
OTHER_OBJS="pa_asio/pa_asio.o pa_win/pa_win_hostapis.o pa_win/pa_win_util.o pa_asio/iasiothiscallresolver.o $ASIODIR/common/asio.o $ASIODIR/host/asiodrivers.o $ASIODIR/host/pc/asiolist.o";
LIBS="-lwinmm -lm -lstdc++ -lole32 -luuid";
PADLL="portaudio.dll";
THREAD_CFLAGS="-mthreads"
SHARED_FLAGS="-shared";
DLL_LIBS="-lwinmm -lm -lstdc++ -lole32 -luuid";
CFLAGS="$CFLAGS -ffast-math -fomit-frame-pointer -Ipa_common -Ipa_asio -I$ASIODIR/host/pc -I$ASIODIR/common -I$ASIODIR/host -DPA_NO_WMME -DPA_NO_DS -DPA_NO_WDMKS -DWINDOWS";
CXXFLAGS="$CFLAGS";
elif [[ $with_winapi = "wdmks" ]] ; then
if [[ $with_dxdir ]] ; then
DXDIR="$with_dxdir";
else
DXDIR="/usr/local/dx7sdk";
fi
echo "DXDIR: $DXDIR"
OTHER_OBJS="pa_win_wdmks/pa_win_wdmks.o pa_win/pa_win_hostapis.o pa_win/pa_win_util.o";
LIBS="-lwinmm -lm -luuid -lsetupapi -lole32";
PADLL="portaudio.dll";
THREAD_CFLAGS="-mthreads"
SHARED_FLAGS="-shared";
DLL_LIBS="-lwinmm -lm -L./dx7sdk/lib -luuid -lsetupapi -lole32";
#VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\"";
#CFLAGS="$CFLAGS -I$VC98 -DPA_NO_WMME -DPA_NO_ASIO";
CFLAGS="$CFLAGS -Ipa_common -I$DXDIR/include -DPA_NO_WMME -DPA_NO_DS -DPA_NO_ASIO";
else # WMME default
OTHER_OBJS="pa_win_wmme/pa_win_wmme.o pa_win/pa_win_hostapis.o pa_win/pa_win_util.o";
LIBS="-lwinmm -lm -lstdc++ -lole32 -luuid";
PADLL="portaudio.dll";
THREAD_CFLAGS="-mthreads"
SHARED_FLAGS="-shared";
DLL_LIBS="-lwinmm";
CFLAGS="$CFLAGS -Ipa_common -DPA_NO_DS -DPA_NO_ASIO -DPA_NO_WDMKS";
fi
;;
cygwin* )
dnl Cygwin configuration
@ -55,27 +195,70 @@ case "${host_os}" in
OTHER_OBJS="pa_win_wmme/pa_win_wmme.o";
LIBS="-lwinmm -lm";
PADLL="portaudio.dll";
SHARED_FLAGS="-shared -mthreads";
THREAD_CFLAGS="-mthreads"
SHARED_FLAGS="-shared";
DLL_LIBS="-lwinmm";
;;
irix* )
dnl SGI IRIX audio library (AL) configuration (Pieter, oct 2-13, 2003).
dnl The 'dmedia' library is needed to read the Unadjusted System Time (UST).
dnl
AC_CHECK_LIB(pthread, pthread_create, , AC_MSG_ERROR([IRIX posix thread library not found!]))
AC_CHECK_LIB(audio, alOpenPort, , AC_MSG_ERROR([IRIX audio library not found!]))
AC_CHECK_LIB(dmedia, dmGetUST, , AC_MSG_ERROR([IRIX digital media library not found!]))
dnl See the '#ifdef PA_USE_SGI' in file pa_unix/pa_unix_hostapis.c
dnl which selects the appropriate PaXXX_Initialize() function.
dnl
AC_DEFINE(PA_USE_SGI)
dnl The _REENTRANT option for pthread safety. Perhaps not necessary but it 'll do no harm.
dnl
THREAD_CFLAGS="-D_REENTRANT"
OTHER_OBJS="pa_sgi/pa_sgi.o pa_unix/pa_unix_hostapis.o pa_unix/pa_unix_util.o";
dnl SGI books say -lpthread should be the last of the libs mentioned.
dnl
LIBS="-lm -ldmedia -laudio -lpthread";
PADLL="libportaudio.so";
SHARED_FLAGS="-shared";
;;
*)
dnl Unix OSS configuration
dnl Unix configuration
AC_CHECK_LIB(pthread, pthread_create,
,
AC_MSG_ERROR([libpthread not found!]))
OTHER_OBJS="pa_unix_oss/pa_unix_oss.o";
LIBS="-lm -lpthread";
if [[ $have_alsa = "yes" ] && [ $with_alsa != "no" ]] ; then
LIBS="$LIBS -lasound"
DLL_LIBS="$DLL_LIBS -lasound"
OTHER_OBJS="$OTHER_OBJS pa_linux_alsa/pa_linux_alsa.o"
AC_DEFINE(PA_USE_ALSA)
fi
if [[ $have_jack = "yes" ] && [ $with_jack != "no" ]] ; then
LIBS="$LIBS $JACK_LIBS"
DLL_LIBS="$DLL_LIBS $JACK_LIBS"
CFLAGS="$CFLAGS $JACK_CFLAGS"
OTHER_OBJS="$OTHER_OBJS pa_jack/pa_jack.o"
AC_DEFINE(PA_USE_JACK)
fi
if [[ $with_oss != "no" ]] ; then
OTHER_OBJS="$OTHER_OBJS pa_unix_oss/pa_unix_oss.o"
AC_DEFINE(PA_USE_OSS)
fi
THREAD_CFLAGS="-pthread"
LIBS="$LIBS -lm -lpthread";
PADLL="libportaudio.so";
SHARED_FLAGS="-shared";
SHARED_FLAGS="-shared -fPIC";
OTHER_OBJS="$OTHER_OBJS pa_unix/pa_unix_hostapis.o pa_unix/pa_unix_util.o"
esac
CFLAGS="$CFLAGS $THREAD_CFLAGS"
AC_OUTPUT([Makefile])
echo ""
echo "Finished configure."
echo ""
echo "Type 'make' to build PortAudio and examples."
AC_OUTPUT([Makefile portaudio-2.0.pc])

View file

@ -21,22 +21,16 @@ PortAudio Documentation</h1></center>
</table></center>
<p>Copyright 2000 Phil Burk and Ross Bencina
<h3>
<a href="pa_tutorial.html">Tutorial</a></h3>
<dl>
<dl>Describes how to write audio programs using the PortAudio API.</dl>
<br>&nbsp;
<h3>
<a href="portaudio_h.txt">API Reference</a></h3>
<dl>The Application Programmer Interface is documented in "portaudio.h".</dl>
<blockquote>The Application Programmer Interface is documented in "portaudio.h".</blockquote>
<h3>
<a href="releases.html">Release Notes</a></h3>
<a href="pa_tutorial.html">Tutorial</a></h3>
<dl>What changed since the last release?</dl>
</dl>
<blockquote>Describes how to write audio programs using the PortAudio API.</blockquote>
<h3>
<a href="pa_impl_guide.html">Implementation Guide</a></h3>

Binary file not shown.

View file

@ -1,156 +0,0 @@
<HTML>
<HEAD>
<TITLE>Coding Style Guidelines for PortAudio Implementors</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
<META content="Phil Burk, Ross Bencina" name=Author>
<META content="coding and design style guide" name=Description>
<META
content="audio, tutorial, library, portable, open-source, DirectSound,sound, music, synthesis, coding, style"
name=KeyWords>
</HEAD>
<BODY LINK="#0000ff" VLINK="#800080">&nbsp;
<CENTER>
<TABLE bgColor=#fada7a cols=1 width="100%">
<TBODY>
<TR>
<TD>
<CENTER>
<H1>Coding Style Guidelines for PortAudio Implementors</H1>
</CENTER>
</TD></TR></TBODY></TABLE></CENTER>
<P><A href="http://www.portaudio.com/">PortAudio Home Page</A></P>
<P>Updated: June 12, 2002 </P>
<H2>Introduction</H2>
<P>
This document provides coding guidelines for PortAudio implementors. Some of the guildelines pertain to mechanical style, others to quality of implementation issues. Since the PortAudio code is commonly edited on many different platforms using different editors these guideline should be followed to improve readability and consistency.
</P>
<H2>PortAudio API Design Guidelines</H2>
<P>
The design guidelines of the PortAudio project are stated here
as some of them apply to implementation as well as to API design.
</P>
<ul>
<li>
Implementation should be possible on all common computer
music platforms.
</li>
<li>
Clients of PortAudio should be able to gain efficient,
and ideally optimal use of the audio services
on all target platforms.
</li>
<li>
The API should be simple enough to be used by music
students with minimal experience in C programming.
</li>
<li>
The API should seek to provide only low-level audio
services, and to only support those services directly
available on the host platform.
</li>
</ul>
<P>
Note that the final guideline has been bent with regard to audio
sample formats and user buffer sizes - PortAudio can convert between
a number of sample formats and can adapt to the user's buffer size requirements.
</P>
<H2>Formatting Conventions</H2>
<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>Line-emd characters will be consistent with the platform on which the source has been checked out of CVS (CVS handles this.)</LI>
<LI>Brace placement will follow ANSI style, eg:
<PRE>
if( aCondition )
{
DoSomething();
}
else
{
DoSomethingElse();
}
</PRE>
</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 don't intend to use it on an ongoing basis. It is expected that contributors of each implementation will take responsibility for keeping their code clean.</P>
<H2>Quality of Implementation Guidelines</H2>
<P>The following coding guidelines should be followed in order 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). It has been suggested that all implementations 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 in some cases, 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 portaudio.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 has well defined behaviour.</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></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. The next section documents naming conventions for all PortAudio symbols.</LI>
<LI>Implementations should minimise their use of static data. </LI></UL>
<H2>Naming Conventions</H2>
<ul>
<li>All #defines begin with PA_</li>
<li>All #defines local to a file end with _</li>
<li>All global utility variables begin with paUtil</li>
<li>All global utility types begin with PaUtil (including function types)</li>
<li>All global utility functions begin with PaUtil_</li>
<li>All static variables end with _</li>
<li>All static constants begin with const and end with _ (eg: constMyMagicNumber_)</li>
<li>All static funtions have no special prefix/suffix</li>
<li>
Platform-specific shared functions should begin with Pa<PN>_ where PN is the
platform name. eg. PaWin_ for windows, PaUnix_ for unix.
</li>
</ul>
<p>
In general, implementations should declare all of their members static,
except for their initializer which should be exported. All exported names
should be preceeded by Pa<MN>_ where <MN> is the module name, for example
the windows mme initializer should be named PaWinMme_Initialize().
</p>
<p>
If it is necessary for implementations to define non-static symbols, they should
use the following naming conventions, where <MN> is the module name such as WinMme.
</p>
<ul>
<li>global variables should begin with pa<MN></li>
<li>global types should begin with Pa<MN></li>
<li>global utility functions should begin with Pa<MN>_</li>
</ul>
<p>
Two utilities for debug messages are provided. The PA_DEBUG macro defined in
pa_implementation.h provides a simple way to print debug messages to stderr.
Due to real-time performance issues, PA_DEBUG may not be suitable for use
within the portaudio processing callback, or in other threads. In such cases
the event tracing facility provided in pa_trace.h may be more appropriate.
</p>
<p>
If PA_LOG_API_CALLS is defined, all calls to the public PortAudio API
will be logged to stderr along with parameter and return values.
</p>
</BODY>
</HTML>

137
pa_asio/ASIO-README.txt Normal file
View file

@ -0,0 +1,137 @@
ASIO-README.txt
This document contains information to help you compile PortAudio with
ASIO support. If you find any omissions or errors in this document
please notify Ross Bencina <rossb@audiomulch.com>.
Building PortAudio with ASIO support
------------------------------------
To build PortAudio with ASIO support you need to compile and link with
pa_asio.c, and files from the ASIO SDK (see below), along with the common
files from pa_common/ and platform specific files from pa_win/ (for Win32)
or pa_mac/ (for Macintosh).
If you are compiling with a non-Microsoft compiler on windows, also
compile and link with iasiothiscallresolver.cpp (see below for
an explanation).
For some platforms (MingW, possibly Mac), you may simply
be able to type:
./configure --with-host_os=mingw --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2]
make
./configure --with-host_os=darwin --with-winapi=asio [--with-asiodir=/usr/local/asiosdk2]
make
and life will be good.
Obtaining the ASIO SDK
----------------------
In order to build PortAudio with ASIO support, you need to download
the ASIO SDK (version 2.0) from Steinberg. Steinberg makes the ASIO
SDK available to anyone free of charge, however they do not permit its
source code to be distributed.
NOTE: In some cases the ASIO SDK may require patching, see below
for further details.
http://www.steinberg.net/en/ps/support/3rdparty/asio_sdk/
If the above link is broken search Google for:
"download steinberg ASIO SDK"
Building the ASIO SDK on Macintosh
----------------------------------
To build the ASIO SDK on Macintosh you need to compile and link with the
following files from the ASIO SDK:
host/asiodrivers.cpp
host/mac/asioshlib.cpp
host/mac/codefragements.cpp
Building the ASIO SDK on Windows
--------------------------------
To build the ASIO SDK on Windows you need to compile and link with the
following files from the ASIO SDK:
asio_sdk\common\asio.cpp
asio_sdk\host\asiodrivers.cpp
asio_sdk\host\pc\asiolist.cpp
You may also need to adjust your include paths to support inclusion of
header files from the above directories.
The ASIO SDK depends on the following COM API functions:
CoInitialize, CoUninitialize, CoCreateInstance, CLSIDFromString
For compilation with MinGW you will need to link with -lole32, for
Borland link with Import32.lib.
Non-Microsoft (MSVC) Compilers on Windows including Borland and GCC
-------------------------------------------------------------------
Steinberg did not specify a calling convention in the IASIO interface
definition. This causes the Microsoft compiler to use the proprietary
thiscall convention which is not compatible with other compilers, such
as compilers from Borland (BCC and C++Builder) and GNU (gcc).
Steinberg's ASIO SDK will compile but crash on initialization if
compiled with a non-Microsoft compiler on Windows.
PortAudio solves this problem using the iasiothiscallresolver library
which is included in the distribution. When building ASIO support for
non-Microsoft compilers, be sure to compile and link with
iasiothiscallresolver.cpp. Note that iasiothiscallresolver includes
conditional directives which cause it to have no effect if it is
compiled with a Microsoft compiler, or on the Macintosh.
If you use configure and make (see above), this should be handled
automatically for you.
For further information about the IASIO thiscall problem see this page:
http://www.audiomulch.com/~rossb/code/calliasio
Macintosh ASIO SDK Bug Patch
----------------------------
There is a bug in the ASIO SDK that causes the Macintosh version to
often fail during initialization. Below 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

@ -1,6 +0,0 @@
Steinberg's ASIO 2 SDK will compile but crash on
initialization if compiled with a Borland compiler.
The problem is described, and a solution provided on
the following page:
http://www.audiomulch.com/~rossb/code/calliasio

View file

@ -0,0 +1,563 @@
/*
IASIOThiscallResolver.cpp see the comments in iasiothiscallresolver.h for
the top level description - this comment describes the technical details of
the implementation.
The latest version of this file is available from:
http://www.audiomulch.com/~rossb/code/calliasio
please email comments to Ross Bencina <rossb@audiomulch.com>
BACKGROUND
The IASIO interface declared in the Steinberg ASIO 2 SDK declares
functions with no explicit calling convention. This causes MSVC++ to default
to using the thiscall convention, which is a proprietary convention not
implemented by some non-microsoft compilers - notably borland BCC,
C++Builder, and gcc. MSVC++ is the defacto standard compiler used by
Steinberg. As a result of this situation, the ASIO sdk will compile with
any compiler, however attempting to execute the compiled code will cause a
crash due to different default calling conventions on non-Microsoft
compilers.
IASIOThiscallResolver solves the problem by providing an adapter class that
delegates to the IASIO interface using the correct calling convention
(thiscall). Due to the lack of support for thiscall in the Borland and GCC
compilers, the calls have been implemented in assembly language.
A number of macros are defined for thiscall function calls with different
numbers of parameters, with and without return values - it may be possible
to modify the format of these macros to make them work with other inline
assemblers.
THISCALL DEFINITION
A number of definitions of the thiscall calling convention are floating
around the internet. The following definition has been validated against
output from the MSVC++ compiler:
For non-vararg functions, thiscall works as follows: the object (this)
pointer is passed in ECX. All arguments are passed on the stack in
right to left order. The return value is placed in EAX. The callee
clears the passed arguments from the stack.
FINDING FUNCTION POINTERS FROM AN IASIO POINTER
The first field of a COM object is a pointer to its vtble. Thus a pointer
to an object implementing the IASIO interface also points to a pointer to
that object's vtbl. The vtble is a table of function pointers for all of
the virtual functions exposed by the implemented interfaces.
If we consider a variable declared as a pointer to IASO:
IASIO *theAsioDriver
theAsioDriver points to:
object implementing IASIO
{
IASIOvtbl *vtbl
other data
}
in other words, theAsioDriver points to a pointer to an IASIOvtbl
vtbl points to a table of function pointers:
IASIOvtbl ( interface IASIO : public IUnknown )
{
(IUnknown functions)
0 virtual HRESULT STDMETHODCALLTYPE (*QueryInterface)(REFIID riid, void **ppv) = 0;
4 virtual ULONG STDMETHODCALLTYPE (*AddRef)() = 0;
8 virtual ULONG STDMETHODCALLTYPE (*Release)() = 0;
(IASIO functions)
12 virtual ASIOBool (*init)(void *sysHandle) = 0;
16 virtual void (*getDriverName)(char *name) = 0;
20 virtual long (*getDriverVersion)() = 0;
24 virtual void (*getErrorMessage)(char *string) = 0;
28 virtual ASIOError (*start)() = 0;
32 virtual ASIOError (*stop)() = 0;
36 virtual ASIOError (*getChannels)(long *numInputChannels, long *numOutputChannels) = 0;
40 virtual ASIOError (*getLatencies)(long *inputLatency, long *outputLatency) = 0;
44 virtual ASIOError (*getBufferSize)(long *minSize, long *maxSize,
long *preferredSize, long *granularity) = 0;
48 virtual ASIOError (*canSampleRate)(ASIOSampleRate sampleRate) = 0;
52 virtual ASIOError (*getSampleRate)(ASIOSampleRate *sampleRate) = 0;
56 virtual ASIOError (*setSampleRate)(ASIOSampleRate sampleRate) = 0;
60 virtual ASIOError (*getClockSources)(ASIOClockSource *clocks, long *numSources) = 0;
64 virtual ASIOError (*setClockSource)(long reference) = 0;
68 virtual ASIOError (*getSamplePosition)(ASIOSamples *sPos, ASIOTimeStamp *tStamp) = 0;
72 virtual ASIOError (*getChannelInfo)(ASIOChannelInfo *info) = 0;
76 virtual ASIOError (*createBuffers)(ASIOBufferInfo *bufferInfos, long numChannels,
long bufferSize, ASIOCallbacks *callbacks) = 0;
80 virtual ASIOError (*disposeBuffers)() = 0;
84 virtual ASIOError (*controlPanel)() = 0;
88 virtual ASIOError (*future)(long selector,void *opt) = 0;
92 virtual ASIOError (*outputReady)() = 0;
};
The numbers in the left column show the byte offset of each function ptr
from the beginning of the vtbl. These numbers are used in the code below
to select different functions.
In order to find the address of a particular function, theAsioDriver
must first be dereferenced to find the value of the vtbl pointer:
mov eax, theAsioDriver
mov edx, [theAsioDriver] // edx now points to vtbl[0]
Then an offset must be added to the vtbl pointer to select a
particular function, for example vtbl+44 points to the slot containing
a pointer to the getBufferSize function.
Finally vtbl+x must be dereferenced to obtain the value of the function
pointer stored in that address:
call [edx+44] // call the function pointed to by
// the value in the getBufferSize field of the vtbl
SEE ALSO
Martin Fay's OpenASIO DLL at http://www.martinfay.com solves the same
problem by providing a new COM interface which wraps IASIO with an
interface that uses portable calling conventions. OpenASIO must be compiled
with MSVC, and requires that you ship the OpenASIO DLL with your
application.
ACKNOWLEDGEMENTS
Ross Bencina: worked out the thiscall details above, wrote the original
Borland asm macros, and a patch for asio.cpp (which is no longer needed).
Thanks to Martin Fay for introducing me to the issues discussed here,
and to Rene G. Ceballos for assisting with asm dumps from MSVC++.
Antti Silvast: converted the original calliasio to work with gcc and NASM
by implementing the asm code in a separate file.
Fraser Adams: modified the original calliasio containing the Borland inline
asm to add inline asm for gcc i.e. Intel syntax for Borland and AT&T syntax
for gcc. This seems a neater approach for gcc than to have a separate .asm
file and it means that we only need one version of the thiscall patch.
Fraser Adams: rewrote the original calliasio patch in the form of the
IASIOThiscallResolver class in order to avoid modifications to files from
the Steinberg SDK, which may have had potential licence issues.
Andrew Baldwin: contributed fixes for compatibility problems with more
recent versions of the gcc assembler.
*/
// We only need IASIOThiscallResolver at all if we are on Win32. For other
// platforms we simply bypass the IASIOThiscallResolver definition to allow us
// to be safely #include'd whatever the platform to keep client code portable
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
// is not used.
#if !defined(_MSC_VER)
#include <new>
#include <assert.h>
// We have a mechanism in iasiothiscallresolver.h to ensure that asio.h is
// #include'd before it in client code, we do NOT want to do this test here.
#define iasiothiscallresolver_sourcefile 1
#include "iasiothiscallresolver.h"
#undef iasiothiscallresolver_sourcefile
// iasiothiscallresolver.h redefines ASIOInit for clients, but we don't want
// this macro defined in this translation unit.
#undef ASIOInit
// theAsioDriver is a global pointer to the current IASIO instance which the
// ASIO SDK uses to perform all actions on the IASIO interface. We substitute
// our own forwarding interface into this pointer.
extern IASIO* theAsioDriver;
// The following macros define the inline assembler for BORLAND first then gcc
#if defined(__BCPLUSPLUS__) || defined(__BORLANDC__)
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset )\
void *this_ = (thisPtr); \
__asm { \
mov ecx, this_ ; \
mov eax, [ecx] ; \
call [eax+funcOffset] ; \
mov resultName, eax ; \
}
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 )\
void *this_ = (thisPtr); \
__asm { \
mov eax, param1 ; \
push eax ; \
mov ecx, this_ ; \
mov eax, [ecx] ; \
call [eax+funcOffset] ; \
}
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 )\
void *this_ = (thisPtr); \
__asm { \
mov eax, param1 ; \
push eax ; \
mov ecx, this_ ; \
mov eax, [ecx] ; \
call [eax+funcOffset] ; \
mov resultName, eax ; \
}
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 )\
void *this_ = (thisPtr); \
void *doubleParamPtr_ (&param1); \
__asm { \
mov eax, doubleParamPtr_ ; \
push [eax+4] ; \
push [eax] ; \
mov ecx, this_ ; \
mov eax, [ecx] ; \
call [eax+funcOffset] ; \
mov resultName, eax ; \
}
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 )\
void *this_ = (thisPtr); \
__asm { \
mov eax, param2 ; \
push eax ; \
mov eax, param1 ; \
push eax ; \
mov ecx, this_ ; \
mov eax, [ecx] ; \
call [eax+funcOffset] ; \
mov resultName, eax ; \
}
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
void *this_ = (thisPtr); \
__asm { \
mov eax, param4 ; \
push eax ; \
mov eax, param3 ; \
push eax ; \
mov eax, param2 ; \
push eax ; \
mov eax, param1 ; \
push eax ; \
mov ecx, this_ ; \
mov eax, [ecx] ; \
call [eax+funcOffset] ; \
mov resultName, eax ; \
}
#elif defined(__GNUC__)
#define CALL_THISCALL_0( resultName, thisPtr, funcOffset ) \
__asm__ __volatile__ ("movl (%1), %%edx\n\t" \
"call *"#funcOffset"(%%edx)\n\t" \
:"=a"(resultName) /* Output Operands */ \
:"c"(thisPtr) /* Input Operands */ \
); \
#define CALL_VOID_THISCALL_1( thisPtr, funcOffset, param1 ) \
__asm__ __volatile__ ("pushl %0\n\t" \
"movl (%1), %%edx\n\t" \
"call *"#funcOffset"(%%edx)\n\t" \
: /* Output Operands */ \
:"r"(param1), /* Input Operands */ \
"c"(thisPtr) \
); \
#define CALL_THISCALL_1( resultName, thisPtr, funcOffset, param1 ) \
__asm__ __volatile__ ("pushl %1\n\t" \
"movl (%2), %%edx\n\t" \
"call *"#funcOffset"(%%edx)\n\t" \
:"=a"(resultName) /* Output Operands */ \
:"r"(param1), /* Input Operands */ \
"c"(thisPtr) \
); \
#define CALL_THISCALL_1_DOUBLE( resultName, thisPtr, funcOffset, param1 ) \
__asm__ __volatile__ ("pushl 4(%1)\n\t" \
"pushl (%1)\n\t" \
"movl (%2), %%edx\n\t" \
"call *"#funcOffset"(%%edx);\n\t" \
:"=a"(resultName) /* Output Operands */ \
:"a"(&param1), /* Input Operands */ \
/* Note: Using "r" above instead of "a" fails */ \
/* when using GCC 3.3.3, and maybe later versions*/\
"c"(thisPtr) \
); \
#define CALL_THISCALL_2( resultName, thisPtr, funcOffset, param1, param2 ) \
__asm__ __volatile__ ("pushl %1\n\t" \
"pushl %2\n\t" \
"movl (%3), %%edx\n\t" \
"call *"#funcOffset"(%%edx)\n\t" \
:"=a"(resultName) /* Output Operands */ \
:"r"(param2), /* Input Operands */ \
"r"(param1), \
"c"(thisPtr) \
); \
#define CALL_THISCALL_4( resultName, thisPtr, funcOffset, param1, param2, param3, param4 )\
__asm__ __volatile__ ("pushl %1\n\t" \
"pushl %2\n\t" \
"pushl %3\n\t" \
"pushl %4\n\t" \
"movl (%5), %%edx\n\t" \
"call *"#funcOffset"(%%edx)\n\t" \
:"=a"(resultName) /* Output Operands */ \
:"r"(param4), /* Input Operands */ \
"r"(param3), \
"r"(param2), \
"r"(param1), \
"c"(thisPtr) \
); \
#endif
// Our static singleton instance.
IASIOThiscallResolver IASIOThiscallResolver::instance;
// Constructor called to initialize static Singleton instance above. Note that
// it is important not to clear that_ incase it has already been set by the call
// to placement new in ASIOInit().
IASIOThiscallResolver::IASIOThiscallResolver()
{
}
// Constructor called from ASIOInit() below
IASIOThiscallResolver::IASIOThiscallResolver(IASIO* that)
: that_( that )
{
}
// Implement IUnknown methods as assert(false). IASIOThiscallResolver is not
// really a COM object, just a wrapper which will work with the ASIO SDK.
// If you wanted to use ASIO without the SDK you might want to implement COM
// aggregation in these methods.
HRESULT STDMETHODCALLTYPE IASIOThiscallResolver::QueryInterface(REFIID riid, void **ppv)
{
(void)riid; // suppress unused variable warning
assert( false ); // this function should never be called by the ASIO SDK.
*ppv = NULL;
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::AddRef()
{
assert( false ); // this function should never be called by the ASIO SDK.
return 1;
}
ULONG STDMETHODCALLTYPE IASIOThiscallResolver::Release()
{
assert( false ); // this function should never be called by the ASIO SDK.
return 1;
}
// Implement the IASIO interface methods by performing the vptr manipulation
// described above then delegating to the real implementation.
ASIOBool IASIOThiscallResolver::init(void *sysHandle)
{
ASIOBool result;
CALL_THISCALL_1( result, that_, 12, sysHandle );
return result;
}
void IASIOThiscallResolver::getDriverName(char *name)
{
CALL_VOID_THISCALL_1( that_, 16, name );
}
long IASIOThiscallResolver::getDriverVersion()
{
ASIOBool result;
CALL_THISCALL_0( result, that_, 20 );
return result;
}
void IASIOThiscallResolver::getErrorMessage(char *string)
{
CALL_VOID_THISCALL_1( that_, 24, string );
}
ASIOError IASIOThiscallResolver::start()
{
ASIOBool result;
CALL_THISCALL_0( result, that_, 28 );
return result;
}
ASIOError IASIOThiscallResolver::stop()
{
ASIOBool result;
CALL_THISCALL_0( result, that_, 32 );
return result;
}
ASIOError IASIOThiscallResolver::getChannels(long *numInputChannels, long *numOutputChannels)
{
ASIOBool result;
CALL_THISCALL_2( result, that_, 36, numInputChannels, numOutputChannels );
return result;
}
ASIOError IASIOThiscallResolver::getLatencies(long *inputLatency, long *outputLatency)
{
ASIOBool result;
CALL_THISCALL_2( result, that_, 40, inputLatency, outputLatency );
return result;
}
ASIOError IASIOThiscallResolver::getBufferSize(long *minSize, long *maxSize,
long *preferredSize, long *granularity)
{
ASIOBool result;
CALL_THISCALL_4( result, that_, 44, minSize, maxSize, preferredSize, granularity );
return result;
}
ASIOError IASIOThiscallResolver::canSampleRate(ASIOSampleRate sampleRate)
{
ASIOBool result;
CALL_THISCALL_1_DOUBLE( result, that_, 48, sampleRate );
return result;
}
ASIOError IASIOThiscallResolver::getSampleRate(ASIOSampleRate *sampleRate)
{
ASIOBool result;
CALL_THISCALL_1( result, that_, 52, sampleRate );
return result;
}
ASIOError IASIOThiscallResolver::setSampleRate(ASIOSampleRate sampleRate)
{
ASIOBool result;
CALL_THISCALL_1_DOUBLE( result, that_, 56, sampleRate );
return result;
}
ASIOError IASIOThiscallResolver::getClockSources(ASIOClockSource *clocks, long *numSources)
{
ASIOBool result;
CALL_THISCALL_2( result, that_, 60, clocks, numSources );
return result;
}
ASIOError IASIOThiscallResolver::setClockSource(long reference)
{
ASIOBool result;
CALL_THISCALL_1( result, that_, 64, reference );
return result;
}
ASIOError IASIOThiscallResolver::getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp)
{
ASIOBool result;
CALL_THISCALL_2( result, that_, 68, sPos, tStamp );
return result;
}
ASIOError IASIOThiscallResolver::getChannelInfo(ASIOChannelInfo *info)
{
ASIOBool result;
CALL_THISCALL_1( result, that_, 72, info );
return result;
}
ASIOError IASIOThiscallResolver::createBuffers(ASIOBufferInfo *bufferInfos,
long numChannels, long bufferSize, ASIOCallbacks *callbacks)
{
ASIOBool result;
CALL_THISCALL_4( result, that_, 76, bufferInfos, numChannels, bufferSize, callbacks );
return result;
}
ASIOError IASIOThiscallResolver::disposeBuffers()
{
ASIOBool result;
CALL_THISCALL_0( result, that_, 80 );
return result;
}
ASIOError IASIOThiscallResolver::controlPanel()
{
ASIOBool result;
CALL_THISCALL_0( result, that_, 84 );
return result;
}
ASIOError IASIOThiscallResolver::future(long selector,void *opt)
{
ASIOBool result;
CALL_THISCALL_2( result, that_, 88, selector, opt );
return result;
}
ASIOError IASIOThiscallResolver::outputReady()
{
ASIOBool result;
CALL_THISCALL_0( result, that_, 92 );
return result;
}
// Implement our substitute ASIOInit() method
ASIOError IASIOThiscallResolver::ASIOInit(ASIODriverInfo *info)
{
// To ensure that our instance's vptr is correctly constructed, even if
// ASIOInit is called prior to main(), we explicitly call its constructor
// (potentially over the top of an existing instance). Note that this is
// pretty ugly, and is only safe because IASIOThiscallResolver has no
// destructor and contains no objects with destructors.
new((void*)&instance) IASIOThiscallResolver( theAsioDriver );
// Interpose between ASIO client code and the real driver.
theAsioDriver = &instance;
// Note that we never need to switch theAsioDriver back to point to the
// real driver because theAsioDriver is reset to zero in ASIOExit().
// Delegate to the real ASIOInit
return ::ASIOInit(info);
}
#endif /* !defined(_MSC_VER) */
#endif /* Win32 */

View file

@ -0,0 +1,197 @@
// ****************************************************************************
// File: IASIOThiscallResolver.h
// Description: The IASIOThiscallResolver class implements the IASIO
// interface and acts as a proxy to the real IASIO interface by
// calling through its vptr table using the thiscall calling
// convention. To put it another way, we interpose
// IASIOThiscallResolver between ASIO SDK code and the driver.
// This is necessary because most non-Microsoft compilers don't
// implement the thiscall calling convention used by IASIO.
//
// iasiothiscallresolver.cpp contains the background of this
// problem plus a technical description of the vptr
// manipulations.
//
// In order to use this mechanism one simply has to add
// iasiothiscallresolver.cpp to the list of files to compile
// and #include <iasiothiscallresolver.h>
//
// Note that this #include must come after the other ASIO SDK
// #includes, for example:
//
// #include <windows.h>
// #include <asiosys.h>
// #include <asio.h>
// #include <asiodrivers.h>
// #include <iasiothiscallresolver.h>
//
// Actually the important thing is to #include
// <iasiothiscallresolver.h> after <asio.h>. We have
// incorporated a test to enforce this ordering.
//
// The code transparently takes care of the interposition by
// using macro substitution to intercept calls to ASIOInit()
// and ASIOExit(). We save the original ASIO global
// "theAsioDriver" in our "that" variable, and then set
// "theAsioDriver" to equal our IASIOThiscallResolver instance.
//
// Whilst this method of resolving the thiscall problem requires
// the addition of #include <iasiothiscallresolver.h> to client
// code it has the advantage that it does not break the terms
// of the ASIO licence by publishing it. We are NOT modifying
// any Steinberg code here, we are merely implementing the IASIO
// interface in the same way that we would need to do if we
// wished to provide an open source ASIO driver.
//
// For compilation with MinGW -lole32 needs to be added to the
// linker options. For BORLAND, linking with Import32.lib is
// sufficient.
//
// The dependencies are with: CoInitialize, CoUninitialize,
// CoCreateInstance, CLSIDFromString - used by asiolist.cpp
// and are required on Windows whether ThiscallResolver is used
// or not.
//
// Searching for the above strings in the root library path
// of your compiler should enable the correct libraries to be
// identified if they aren't immediately obvious.
//
// Note that the current implementation of IASIOThiscallResolver
// is not COM compliant - it does not correctly implement the
// IUnknown interface. Implementing it is not necessary because
// it is not called by parts of the ASIO SDK which call through
// theAsioDriver ptr. The IUnknown methods are implemented as
// assert(false) to ensure that the code fails if they are
// ever called.
// Restrictions: None. Public Domain & Open Source distribute freely
// You may use IASIOThiscallResolver commercially as well as
// privately.
// You the user assume the responsibility for the use of the
// files, binary or text, and there is no guarantee or warranty,
// expressed or implied, including but not limited to the
// implied warranties of merchantability and fitness for a
// particular purpose. You assume all responsibility and agree
// to hold no entity, copyright holder or distributors liable
// for any loss of data or inaccurate representations of data
// as a result of using IASIOThiscallResolver.
// Version: 1.4 Added separate macro CALL_THISCALL_1_DOUBLE from
// Andrew Baldwin, and volatile for whole gcc asm blocks,
// both for compatibility with newer gcc versions. Cleaned up
// Borland asm to use one less register.
// 1.3 Switched to including assert.h for better compatibility.
// Wrapped entire .h and .cpp contents with a check for
// _MSC_VER to provide better compatibility with MS compilers.
// Changed Singleton implementation to use static instance
// instead of freestore allocated instance. Removed ASIOExit
// macro as it is no longer needed.
// 1.2 Removed semicolons from ASIOInit and ASIOExit macros to
// allow them to be embedded in expressions (if statements).
// Cleaned up some comments. Removed combase.c dependency (it
// doesn't compile with BCB anyway) by stubbing IUnknown.
// 1.1 Incorporated comments from Ross Bencina including things
// such as changing name from ThiscallResolver to
// IASIOThiscallResolver, tidying up the constructor, fixing
// a bug in IASIOThiscallResolver::ASIOExit() and improving
// portability through the use of conditional compilation
// 1.0 Initial working version.
// Created: 6/09/2003
// Authors: Fraser Adams
// Ross Bencina
// Rene G. Ceballos
// Martin Fay
// Antti Silvast
// Andrew Baldwin
//
// ****************************************************************************
#ifndef included_iasiothiscallresolver_h
#define included_iasiothiscallresolver_h
// We only need IASIOThiscallResolver at all if we are on Win32. For other
// platforms we simply bypass the IASIOThiscallResolver definition to allow us
// to be safely #include'd whatever the platform to keep client code portable
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
// If microsoft compiler we can call IASIO directly so IASIOThiscallResolver
// is not used.
#if !defined(_MSC_VER)
// The following is in order to ensure that this header is only included after
// the other ASIO headers (except for the case of iasiothiscallresolver.cpp).
// We need to do this because IASIOThiscallResolver works by eclipsing the
// original definition of ASIOInit() with a macro (see below).
#if !defined(iasiothiscallresolver_sourcefile)
#if !defined(__ASIO_H)
#error iasiothiscallresolver.h must be included AFTER asio.h
#endif
#endif
#include <windows.h>
#include <asiodrvr.h> /* From ASIO SDK */
class IASIOThiscallResolver : public IASIO {
private:
IASIO* that_; // Points to the real IASIO
static IASIOThiscallResolver instance; // Singleton instance
// Constructors - declared private so construction is limited to
// our Singleton instance
IASIOThiscallResolver();
IASIOThiscallResolver(IASIO* that);
public:
// Methods from the IUnknown interface. We don't fully implement IUnknown
// because the ASIO SDK never calls these methods through theAsioDriver ptr.
// These methods are implemented as assert(false).
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
// Methods from the IASIO interface, implemented as forwarning calls to that.
virtual ASIOBool init(void *sysHandle);
virtual void getDriverName(char *name);
virtual long getDriverVersion();
virtual void getErrorMessage(char *string);
virtual ASIOError start();
virtual ASIOError stop();
virtual ASIOError getChannels(long *numInputChannels, long *numOutputChannels);
virtual ASIOError getLatencies(long *inputLatency, long *outputLatency);
virtual ASIOError getBufferSize(long *minSize, long *maxSize, long *preferredSize, long *granularity);
virtual ASIOError canSampleRate(ASIOSampleRate sampleRate);
virtual ASIOError getSampleRate(ASIOSampleRate *sampleRate);
virtual ASIOError setSampleRate(ASIOSampleRate sampleRate);
virtual ASIOError getClockSources(ASIOClockSource *clocks, long *numSources);
virtual ASIOError setClockSource(long reference);
virtual ASIOError getSamplePosition(ASIOSamples *sPos, ASIOTimeStamp *tStamp);
virtual ASIOError getChannelInfo(ASIOChannelInfo *info);
virtual ASIOError createBuffers(ASIOBufferInfo *bufferInfos, long numChannels, long bufferSize, ASIOCallbacks *callbacks);
virtual ASIOError disposeBuffers();
virtual ASIOError controlPanel();
virtual ASIOError future(long selector,void *opt);
virtual ASIOError outputReady();
// Class method, see ASIOInit() macro below.
static ASIOError ASIOInit(ASIODriverInfo *info); // Delegates to ::ASIOInit
};
// Replace calls to ASIOInit with our interposing version.
// This macro enables us to perform thiscall resolution simply by #including
// <iasiothiscallresolver.h> after the asio #includes (this file _must_ be
// included _after_ the asio #includes)
#define ASIOInit(name) IASIOThiscallResolver::ASIOInit((name))
#endif /* !defined(_MSC_VER) */
#endif /* Win32 */
#endif /* included_iasiothiscallresolver_h */

File diff suppressed because it is too large Load diff

122
pa_asio/pa_asio.h Normal file
View file

@ -0,0 +1,122 @@
#ifndef PA_ASIO_H
#define PA_ASIO_H
/*
* $Id$
* PortAudio Portable Real-Time Audio Library
* ASIO specific extensions
*
* 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.
*
*/
/** @file
@brief ASIO-specific PortAudio API extension header file.
*/
#include "portaudio.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/** Retrieve legal latency settings for the specificed device, in samples.
@param device The global index of the device about which the query is being made.
@param minLatency A pointer to the location which will recieve the minimum latency value.
@param maxLatency A pointer to the location which will recieve the maximum latency value.
@param preferredLatency A pointer to the location which will recieve the preferred latency value.
@param granularity A pointer to the location which will recieve the granularity. This value
determines which values between minLatency and maxLatency are available. ie the step size,
if granularity is -1 then available latency settings are powers of two.
@see ASIOGetBufferSize in the ASIO SDK.
@todo This function should have a better name, any suggestions?
*/
PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
long *minLatency, long *maxLatency, long *preferredLatency, long *granularity );
/** Display the ASIO control panel for the specified device.
@param device The global index of the device whose control panel is to be displayed.
@param systemSpecific On Windows, the calling application's main window handle,
on Macintosh this value should be zero.
*/
PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific );
/** Retrieve a pointer to a string containing the name of the specified
input channel. The string is valid until Pa_Terminate is called.
The string will be no longer than 32 characters including the null terminator.
*/
PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex,
const char** channelName );
/** Retrieve a pointer to a string containing the name of the specified
input channel. The string is valid until Pa_Terminate is called.
The string will be no longer than 32 characters including the null terminator.
*/
PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex,
const char** channelName );
#define paAsioUseChannelSelectors (0x01)
typedef struct PaAsioStreamInfo{
unsigned long size; /**< sizeof(PaAsioStreamInfo) */
PaHostApiTypeId hostApiType; /**< paASIO */
unsigned long version; /**< 1 */
unsigned long flags;
/* Support for opening only specific channels of an ASIO device.
If the paAsioUseChannelSelectors flag is set, channelSelectors is a
pointer to an array of integers specifying the device channels to use.
When used, the length of the channelSelectors array must match the
corresponding channelCount parameter to Pa_OpenStream() otherwise a
crash may result.
The values in the selectors array must specify channels within the
range of supported channels for the device or paInvalidChannelCount will
result.
*/
int *channelSelectors;
}PaAsioStreamInfo;
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_ASIO_H */

View file

@ -1,25 +0,0 @@
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;
}

234
pa_common/pa_allocation.c Normal file
View file

@ -0,0 +1,234 @@
/*
* $Id$
* Portable Audio I/O Library allocation group implementation
* memory allocation group for tracking allocation groups
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, 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.
*/
/** @file
@brief Allocation Group implementation.
*/
#include "pa_allocation.h"
#include "pa_util.h"
/*
Maintain 3 singly linked lists...
linkBlocks: the buffers used to allocate the links
spareLinks: links available for use in the allocations list
allocations: the buffers currently allocated using PaUtil_ContextAllocateMemory()
Link block size is doubled every time new links are allocated.
*/
#define PA_INITIAL_LINK_COUNT_ 16
struct PaUtilAllocationGroupLink
{
struct PaUtilAllocationGroupLink *next;
void *buffer;
};
/*
Allocate a block of links. The first link will have it's buffer member
pointing to the block, and it's next member set to <nextBlock>. The remaining
links will have NULL buffer members, and each link will point to
the next link except the last, which will point to <nextSpare>
*/
static struct PaUtilAllocationGroupLink *AllocateLinks( long count,
struct PaUtilAllocationGroupLink *nextBlock,
struct PaUtilAllocationGroupLink *nextSpare )
{
struct PaUtilAllocationGroupLink *result;
int i;
result = (struct PaUtilAllocationGroupLink *)PaUtil_AllocateMemory(
sizeof(struct PaUtilAllocationGroupLink) * count );
if( result )
{
/* the block link */
result[0].buffer = result;
result[0].next = nextBlock;
/* the spare links */
for( i=1; i<count; ++i )
{
result[i].buffer = 0;
result[i].next = &result[i+1];
}
result[count-1].next = nextSpare;
}
return result;
}
PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void )
{
PaUtilAllocationGroup* result = 0;
struct PaUtilAllocationGroupLink *links;
links = AllocateLinks( PA_INITIAL_LINK_COUNT_, 0, 0 );
if( links != 0 )
{
result = (PaUtilAllocationGroup*)PaUtil_AllocateMemory( sizeof(PaUtilAllocationGroup) );
if( result )
{
result->linkCount = PA_INITIAL_LINK_COUNT_;
result->linkBlocks = &links[0];
result->spareLinks = &links[1];
result->allocations = 0;
}
else
{
PaUtil_FreeMemory( links );
}
}
return result;
}
void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group )
{
struct PaUtilAllocationGroupLink *current = group->linkBlocks;
struct PaUtilAllocationGroupLink *next;
while( current )
{
next = current->next;
PaUtil_FreeMemory( current->buffer );
current = next;
}
PaUtil_FreeMemory( group );
}
void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size )
{
struct PaUtilAllocationGroupLink *links, *link;
void *result = 0;
/* allocate more links if necessary */
if( !group->spareLinks )
{
/* double the link count on each block allocation */
links = AllocateLinks( group->linkCount, group->linkBlocks, group->spareLinks );
if( links )
{
group->linkCount += group->linkCount;
group->linkBlocks = &links[0];
group->spareLinks = &links[1];
}
}
if( group->spareLinks )
{
result = PaUtil_AllocateMemory( size );
if( result )
{
link = group->spareLinks;
group->spareLinks = link->next;
link->buffer = result;
link->next = group->allocations;
group->allocations = link;
}
}
return result;
}
void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer )
{
struct PaUtilAllocationGroupLink *current = group->allocations;
struct PaUtilAllocationGroupLink *previous = 0;
if( buffer == 0 )
return;
/* find the right link and remove it */
while( current )
{
if( current->buffer == buffer )
{
if( previous )
{
previous->next = current->next;
}
else
{
group->allocations = current->next;
}
current->buffer = 0;
current->next = group->spareLinks;
group->spareLinks = current;
break;
}
previous = current;
current = current->next;
}
PaUtil_FreeMemory( buffer ); /* free the memory whether we found it in the list or not */
}
void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group )
{
struct PaUtilAllocationGroupLink *current = group->allocations;
struct PaUtilAllocationGroupLink *previous = 0;
/* free all buffers in the allocations list */
while( current )
{
PaUtil_FreeMemory( current->buffer );
current->buffer = 0;
previous = current;
current = current->next;
}
/* link the former allocations list onto the front of the spareLinks list */
if( previous )
{
previous->next = group->spareLinks;
group->spareLinks = group->allocations;
group->allocations = 0;
}
}

95
pa_common/pa_allocation.h Normal file
View file

@ -0,0 +1,95 @@
#ifndef PA_ALLOCATION_H
#define PA_ALLOCATION_H
/*
* $Id$
* Portable Audio I/O Library allocation context header
* memory allocation context for tracking allocation groups
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, 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.
*/
/** @file
@brief Allocation Group prototypes. An Allocation Group makes it easy to
allocate multiple blocks of memory and free them all simultanously.
An allocation group is useful for keeping track of multiple blocks
of memory which are allocated at the same time (such as during initialization)
and need to be deallocated at the same time. The allocation group maintains
a list of allocated blocks, and can deallocate them all simultaneously which
can be usefull for cleaning up after a partially initialized object fails.
The allocation group implementation is built on top of the lower
level allocation functions defined in pa_util.h
*/
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef struct
{
long linkCount;
struct PaUtilAllocationGroupLink *linkBlocks;
struct PaUtilAllocationGroupLink *spareLinks;
struct PaUtilAllocationGroupLink *allocations;
}PaUtilAllocationGroup;
/** Create an allocation group.
*/
PaUtilAllocationGroup* PaUtil_CreateAllocationGroup( void );
/** Destroy an allocation group, but not the memory allocated through the group.
*/
void PaUtil_DestroyAllocationGroup( PaUtilAllocationGroup* group );
/** Allocate a block of memory though an allocation group.
*/
void* PaUtil_GroupAllocateMemory( PaUtilAllocationGroup* group, long size );
/** Free a block of memory that was previously allocated though an allocation
group. Calling this function is a relatively time consuming operation.
Under normal circumstances clients should call PaUtil_FreeAllAllocations to
free all allocated blocks simultaneously.
@see PaUtil_FreeAllAllocations
*/
void PaUtil_GroupFreeMemory( PaUtilAllocationGroup* group, void *buffer );
/** Free all blocks of memory which have been allocated through the allocation
group. This function doesn't destroy the group itself.
*/
void PaUtil_FreeAllAllocations( PaUtilAllocationGroup* group );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_ALLOCATION_H */

View file

@ -1,402 +0,0 @@
/*
* 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;
}

1926
pa_common/pa_converters.c Normal file

File diff suppressed because it is too large Load diff

254
pa_common/pa_converters.h Normal file
View file

@ -0,0 +1,254 @@
#ifndef PA_CONVERTERS_H
#define PA_CONVERTERS_H
/*
* $Id$
* Portable Audio I/O Library sample conversion mechanism
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Phil Burk, 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.
*/
/** @file
@brief Conversion functions used to convert buffers of samples from one
format to another.
*/
#include "portaudio.h" /* for PaSampleFormat */
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
struct PaUtilTriangularDitherGenerator;
/** Choose an available sample format which is most appropriate for
representing the requested format. If the requested format is not available
higher quality formats are considered before lower quality formates.
@param availableFormats A variable containing the logical OR of all available
formats.
@param format The desired format.
@return The most appropriate available format for representing the requested
format.
*/
PaSampleFormat PaUtil_SelectClosestAvailableFormat(
PaSampleFormat availableFormats, PaSampleFormat format );
/* high level conversions functions for use by implementations */
/** The generic sample converter prototype. Sample converters convert count
samples from sourceBuffer to destinationBuffer. The actual type of the data
pointed to by these parameters varys for different converter functions.
@param destinationBuffer A pointer to the first sample of the destination.
@param destinationStride An offset between successive destination samples
expressed in samples (not bytes.) It may be negative.
@param sourceBuffer A pointer to the first sample of the source.
@param sourceStride An offset between successive source samples
expressed in samples (not bytes.) It may be negative.
@param count The number of samples to convert.
@param ditherState State information used to calculate dither. Converters
that do not perform dithering will ignore this parameter, in which case
NULL or invalid dither state may be passed.
*/
typedef void PaUtilConverter(
void *destinationBuffer, signed int destinationStride,
void *sourceBuffer, signed int sourceStride,
unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator );
/** Find a sample converter function for the given source and destinations
formats and flags (clip and dither.)
@return
A pointer to a PaUtilConverter which will perform the requested
conversion, or NULL if the given format conversion is not supported.
For conversions where clipping or dithering is not necessary, the
clip and dither flags are ignored and a non-clipping or dithering
version is returned.
If the source and destination formats are the same, a function which
copies data of the appropriate size will be returned.
*/
PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
PaSampleFormat destinationFormat, PaStreamFlags flags );
/** The generic buffer zeroer prototype. Buffer zeroers copy count zeros to
destinationBuffer. The actual type of the data pointed to varys for
different zeroer functions.
@param destinationBuffer A pointer to the first sample of the destination.
@param destinationStride An offset between successive destination samples
expressed in samples (not bytes.) It may be negative.
@param count The number of samples to zero.
*/
typedef void PaUtilZeroer(
void *destinationBuffer, signed int destinationStride, unsigned int count );
/** Find a buffer zeroer function for the given destination format.
@return
A pointer to a PaUtilZeroer which will perform the requested
zeroing.
*/
PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat );
/*----------------------------------------------------------------------------*/
/* low level functions and data structures which may be used for
substituting conversion functions */
/** The type used to store all sample conversion functions.
@see paConverters;
*/
typedef struct{
PaUtilConverter *Float32_To_Int32;
PaUtilConverter *Float32_To_Int32_Dither;
PaUtilConverter *Float32_To_Int32_Clip;
PaUtilConverter *Float32_To_Int32_DitherClip;
PaUtilConverter *Float32_To_Int24;
PaUtilConverter *Float32_To_Int24_Dither;
PaUtilConverter *Float32_To_Int24_Clip;
PaUtilConverter *Float32_To_Int24_DitherClip;
PaUtilConverter *Float32_To_Int16;
PaUtilConverter *Float32_To_Int16_Dither;
PaUtilConverter *Float32_To_Int16_Clip;
PaUtilConverter *Float32_To_Int16_DitherClip;
PaUtilConverter *Float32_To_Int8;
PaUtilConverter *Float32_To_Int8_Dither;
PaUtilConverter *Float32_To_Int8_Clip;
PaUtilConverter *Float32_To_Int8_DitherClip;
PaUtilConverter *Float32_To_UInt8;
PaUtilConverter *Float32_To_UInt8_Dither;
PaUtilConverter *Float32_To_UInt8_Clip;
PaUtilConverter *Float32_To_UInt8_DitherClip;
PaUtilConverter *Int32_To_Float32;
PaUtilConverter *Int32_To_Int24;
PaUtilConverter *Int32_To_Int24_Dither;
PaUtilConverter *Int32_To_Int16;
PaUtilConverter *Int32_To_Int16_Dither;
PaUtilConverter *Int32_To_Int8;
PaUtilConverter *Int32_To_Int8_Dither;
PaUtilConverter *Int32_To_UInt8;
PaUtilConverter *Int32_To_UInt8_Dither;
PaUtilConverter *Int24_To_Float32;
PaUtilConverter *Int24_To_Int32;
PaUtilConverter *Int24_To_Int16;
PaUtilConverter *Int24_To_Int16_Dither;
PaUtilConverter *Int24_To_Int8;
PaUtilConverter *Int24_To_Int8_Dither;
PaUtilConverter *Int24_To_UInt8;
PaUtilConverter *Int24_To_UInt8_Dither;
PaUtilConverter *Int16_To_Float32;
PaUtilConverter *Int16_To_Int32;
PaUtilConverter *Int16_To_Int24;
PaUtilConverter *Int16_To_Int8;
PaUtilConverter *Int16_To_Int8_Dither;
PaUtilConverter *Int16_To_UInt8;
PaUtilConverter *Int16_To_UInt8_Dither;
PaUtilConverter *Int8_To_Float32;
PaUtilConverter *Int8_To_Int32;
PaUtilConverter *Int8_To_Int24;
PaUtilConverter *Int8_To_Int16;
PaUtilConverter *Int8_To_UInt8;
PaUtilConverter *UInt8_To_Float32;
PaUtilConverter *UInt8_To_Int32;
PaUtilConverter *UInt8_To_Int24;
PaUtilConverter *UInt8_To_Int16;
PaUtilConverter *UInt8_To_Int8;
PaUtilConverter *Copy_8_To_8; /* copy without any conversion */
PaUtilConverter *Copy_16_To_16; /* copy without any conversion */
PaUtilConverter *Copy_24_To_24; /* copy without any conversion */
PaUtilConverter *Copy_32_To_32; /* copy without any conversion */
} PaUtilConverterTable;
/** A table of pointers to all required converter functions.
PaUtil_SelectConverter() uses this table to lookup the appropriate
conversion functions. The fields of this structure are initialized
with default conversion functions. Fields may be NULL, indicating that
no conversion function is available. User code may substitue optimised
conversion functions by assigning different function pointers to
these fields.
@note
If the PA_NO_STANDARD_CONVERTERS preprocessor variable is defined,
PortAudio's standard converters will not be compiled, and all fields
of this structure will be initialized to NULL. In such cases, users
should supply their own conversion functions if the require PortAudio
to open a stream that requires sample conversion.
@see PaUtilConverterTable, PaUtilConverter, PaUtil_SelectConverter
*/
extern PaUtilConverterTable paConverters;
/** The type used to store all buffer zeroing functions.
@see paZeroers;
*/
typedef struct{
PaUtilZeroer *ZeroU8; /* unsigned 8 bit, zero == 128 */
PaUtilZeroer *Zero8;
PaUtilZeroer *Zero16;
PaUtilZeroer *Zero24;
PaUtilZeroer *Zero32;
} PaUtilZeroerTable;
/** A table of pointers to all required zeroer functions.
PaUtil_SelectZeroer() uses this table to lookup the appropriate
conversion functions. The fields of this structure are initialized
with default conversion functions. User code may substitue optimised
conversion functions by assigning different function pointers to
these fields.
@note
If the PA_NO_STANDARD_ZEROERS preprocessor variable is defined,
PortAudio's standard zeroers will not be compiled, and all fields
of this structure will be initialized to NULL. In such cases, users
should supply their own zeroing functions for the sample sizes which
they intend to use.
@see PaUtilZeroerTable, PaUtilZeroer, PaUtil_SelectZeroer
*/
extern PaUtilZeroerTable paZeroers;
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_CONVERTERS_H */

96
pa_common/pa_cpuload.c Normal file
View file

@ -0,0 +1,96 @@
/*
* $Id$
* Portable Audio I/O Library CPU Load measurement functions
* Portable CPU load measurement facility.
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 2002 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.
*/
/** @file
@brief Functions to assist in measuring the CPU utilization of a callback
stream. Used to implement the Pa_GetStreamCpuLoad() function.
@todo Dynamically calculate the coefficients used to smooth the CPU Load
Measurements over time to provide a uniform characterisation of CPU Load
independent of rate at which PaUtil_BeginCpuLoadMeasurement /
PaUtil_EndCpuLoadMeasurement are called.
*/
#include "pa_cpuload.h"
#include <assert.h>
#include "pa_util.h" /* for PaUtil_GetTime() */
void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate )
{
assert( sampleRate > 0 );
measurer->samplingPeriod = 1. / sampleRate;
measurer->averageLoad = 0.;
}
void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer )
{
measurer->averageLoad = 0.;
}
void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer )
{
measurer->measurementStartTime = PaUtil_GetTime();
}
void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed )
{
double measurementEndTime, secondsFor100Percent, measuredLoad;
if( framesProcessed > 0 ){
measurementEndTime = PaUtil_GetTime();
assert( framesProcessed > 0 );
secondsFor100Percent = framesProcessed * measurer->samplingPeriod;
measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent;
/* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */
/** FIXME @todo these coefficients shouldn't be hardwired */
#define LOWPASS_COEFFICIENT_0 (0.9)
#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0)
measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) +
(LOWPASS_COEFFICIENT_1 * measuredLoad);
}
}
double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer )
{
return measurer->averageLoad;
}

63
pa_common/pa_cpuload.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef PA_CPULOAD_H
#define PA_CPULOAD_H
/*
* $Id$
* Portable Audio I/O Library CPU Load measurement functions
* Portable CPU load measurement facility.
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 2002 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.
*/
/** @file
@brief Functions to assist in measuring the CPU utilization of a callback
stream. Used to implement the Pa_GetStreamCpuLoad() function.
*/
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
typedef struct {
double samplingPeriod;
double measurementStartTime;
double averageLoad;
} PaUtilCpuLoadMeasurer; /**< @todo need better name than measurer */
void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate );
void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer );
void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed );
void PaUtil_ResetCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer );
double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_CPULOAD_H */

204
pa_common/pa_dither.c Normal file
View file

@ -0,0 +1,204 @@
/*
* $Id$
* Portable Audio I/O Library triangular dither generator
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Phil Burk, 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.
*/
/** @file
@brief Functions for generating dither noise
*/
#include "pa_dither.h"
#include "pa_types.h"
#define PA_DITHER_BITS_ (15)
void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state )
{
state->previous = 0;
state->randSeed1 = 22222;
state->randSeed2 = 5555555;
}
signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state )
{
signed long current, highPass;
/* Generate two random numbers. */
state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
state->randSeed2 = (state->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_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
(((signed long)state->randSeed2)>>DITHER_SHIFT_);
/* High pass filter to reduce audibility. */
highPass = current - state->previous;
state->previous = current;
return highPass;
}
/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */
#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<<PA_DITHER_BITS_)-1))
static const float const_float_dither_scale_ = PA_FLOAT_DITHER_SCALE_;
float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *state )
{
signed long current, highPass;
/* Generate two random numbers. */
state->randSeed1 = (state->randSeed1 * 196314165) + 907633515;
state->randSeed2 = (state->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_ ((SIZEOF_LONG*8 - PA_DITHER_BITS_) + 1)
current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) +
(((signed long)state->randSeed2)>>DITHER_SHIFT_);
/* High pass filter to reduce audibility. */
highPass = current - state->previous;
state->previous = current;
return ((float)highPass) * const_float_dither_scale_;
}
/*
The following alternate dither algorithms (from musicdsp.org) could be
considered
*/
/*Noise shaped dither (March 2000)
-------------------
This is a simple implementation of highpass triangular-PDF dither with
2nd-order noise shaping, for use when truncating floating point audio
data to fixed point.
The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz
sample rate) compared to triangular-PDF dither. The code below assumes
input data is in the range +1 to -1 and doesn't check for overloads!
To save time when generating dither for multiple channels you can do
things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again.
int r1, r2; //rectangular-PDF random numbers
float s1, s2; //error feedback buffers
float s = 0.5f; //set to 0.0f for no noise shaping
float w = pow(2.0,bits-1); //word length (usually bits=16)
float wi= 1.0f/w;
float d = wi / RAND_MAX; //dither amplitude (2 lsb)
float o = wi * 0.5f; //remove dc offset
float in, tmp;
int out;
//for each sample...
r2=r1; //can make HP-TRI dither by
r1=rand(); //subtracting previous rand()
in += s * (s1 + s1 - s2); //error feedback
tmp = in + o + d * (float)(r1 - r2); //dc offset and dither
out = (int)(w * tmp); //truncate downwards
if(tmp<0.0f) out--; //this is faster than floor()
s2 = s1;
s1 = in - wi * (float)out; //error
--
paul.kellett@maxim.abel.co.uk
http://www.maxim.abel.co.uk
*/
/*
16-to-8-bit first-order dither
Type : First order error feedforward dithering code
References : Posted by Jon Watte
Notes :
This is about as simple a dithering algorithm as you can implement, but it's
likely to sound better than just truncating to N bits.
Note that you might not want to carry forward the full difference for infinity.
It's probably likely that the worst performance hit comes from the saturation
conditionals, which can be avoided with appropriate instructions on many DSPs
and integer SIMD type instructions, or CMOV.
Last, if sound quality is paramount (such as when going from > 16 bits to 16
bits) you probably want to use a higher-order dither function found elsewhere
on this site.
Code :
// This code will down-convert and dither a 16-bit signed short
// mono signal into an 8-bit unsigned char signal, using a first
// order forward-feeding error term dither.
#define uchar unsigned char
void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory )
{
int m = *memory;
while( count-- > 0 ) {
int i = *input++;
i += m;
int j = i + 32768 - 128;
uchar o;
if( j < 0 ) {
o = 0;
}
else if( j > 65535 ) {
o = 255;
}
else {
o = (uchar)((j>>8)&0xff);
}
m = ((j-32768+128)-i);
*output++ = o;
}
*memory = m;
}
*/

91
pa_common/pa_dither.h Normal file
View file

@ -0,0 +1,91 @@
#ifndef PA_DITHER_H
#define PA_DITHER_H
/*
* $Id$
* Portable Audio I/O Library triangular dither generator
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Phil Burk, 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.
*/
/** @file
@brief Functions for generating dither noise
*/
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/** @brief State needed to generate a dither signal */
typedef struct PaUtilTriangularDitherGenerator{
unsigned long previous;
unsigned long randSeed1;
unsigned long randSeed2;
} PaUtilTriangularDitherGenerator;
/** @brief Initialize dither state */
void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState );
/**
@brief Calculate 2 LSB dither signal with a triangular distribution.
Ranged for adding to a 1 bit right-shifted 32 bit integer
prior to >>15. eg:
<pre>
signed long in = *
signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
signed short out = (signed short)(((in>>1) + dither) >> 15);
</pre>
@return
A signed long with a range of +32767 to -32768
*/
signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
/**
@brief Calculate 2 LSB dither signal with a triangular distribution.
Ranged for adding to a pre-scaled float.
<pre>
float in = *
float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
// use smaller scaler to prevent overflow when we add the dither
signed short out = (signed short)(in*(32766.0f) + dither );
</pre>
@return
A float with a range of -2.0 to +1.99999.
*/
float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState );
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_DITHER_H */

130
pa_common/pa_endianness.h Normal file
View file

@ -0,0 +1,130 @@
#ifndef PA_ENDIANNESS_H
#define PA_ENDIANNESS_H
/*
* $Id$
* Portable Audio I/O Library current platform endianness macros
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Phil Burk, 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.
*/
/** @file
@brief Configure endianness symbols for the target processor.
Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols
to be defined. The one that is defined reflects the endianness of the target
platform and may be used to implement conditional compilation of byte-order
dependent code.
If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt
is made to override that setting. This may be useful if you have a better way
of determining the platform's endianness. The autoconf mechanism uses this for
example.
A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time
and runtime endiannes and raise an assertion if they don't match.
*/
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/* If this is an apple, we need to do detect endianness this way */
#if defined(__APPLE__)
/* we need to do some endian detection that is sensitive to harware arch */
#if defined(__LITTLE_ENDIAN__)
#if !defined( PA_LITTLE_ENDIAN )
#define PA_LITTLE_ENDIAN
#endif
#if defined( PA_BIG_ENDIAN )
#undef PA_BIG_ENDIAN
#endif
#else
#if !defined( PA_BIG_ENDIAN )
#define PA_BIG_ENDIAN
#endif
#if defined( PA_LITTLE_ENDIAN )
#undef PA_LITTLE_ENDIAN
#endif
#endif
#else
/* this is not an apple, so first check the existing defines, and, failing that,
detect well-known architechtures. */
#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN)
/* endianness define has been set externally, such as by autoconf */
#if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN)
#error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please
#endif
#endif
/* endianness define has not been set externally */
/* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(LITTLE_ENDIAN) || defined(__i386) || defined(_M_IX86)
#define PA_LITTLE_ENDIAN /* win32, assume intel byte order */
#else
#define PA_BIG_ENDIAN
#endif
#if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN)
/*
If the following error is raised, you either need to modify the code above
to automatically determine the endianness from other symbols defined on your
platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally.
*/
#error pa_endianness.h was unable to automatically determine the endianness of the target platform
#endif
#endif
/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness,
and raises an assertion if they don't match. <assert.h> must be included in
the context in which this macro is used.
*/
#if defined(PA_LITTLE_ENDIAN)
#define PA_VALIDATE_ENDIANNESS \
{ \
const long nativeOne = 1; \
assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \
}
#elif defined(PA_BIG_ENDIAN)
#define PA_VALIDATE_ENDIANNESS \
{ \
const long nativeOne = 1; \
assert( "PortAudio: compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \
}
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_ENDIANNESS_H */

1981
pa_common/pa_front.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,185 +0,0 @@
#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 */

244
pa_common/pa_hostapi.h Normal file
View file

@ -0,0 +1,244 @@
#ifndef PA_HOSTAPI_H
#define PA_HOSTAPI_H
/*
* $Id$
* Portable Audio I/O Library
* host api representation
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, 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.
*/
/** @file
@brief Interface used by pa_front to virtualize functions which operate on
host APIs.
*/
#include "portaudio.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/** **FOR THE USE OF pa_front.c ONLY**
Do NOT use fields in this structure, they my change at any time.
Use functions defined in pa_util.h if you think you need functionality
which can be derived from here.
*/
typedef struct PaUtilPrivatePaFrontHostApiInfo {
unsigned long baseDeviceIndex;
}PaUtilPrivatePaFrontHostApiInfo;
/** The common header for all data structures whose pointers are passed through
the hostApiSpecificStreamInfo field of the PaStreamParameters structure.
Note that in order to keep the public PortAudio interface clean, this structure
is not used explicitly when declaring hostApiSpecificStreamInfo data structures.
However, some code in pa_front depends on the first 3 members being equivalent
with this structure.
@see PaStreamParameters
*/
typedef struct PaUtilHostApiSpecificStreamInfoHeader
{
unsigned long size; /**< size of whole structure including this header */
PaHostApiTypeId hostApiType; /**< host API for which this data is intended */
unsigned long version; /**< structure version */
} PaUtilHostApiSpecificStreamInfoHeader;
/** A structure representing the interface to a host API. Contains both
concrete data and pointers to functions which implement the interface.
*/
typedef struct PaUtilHostApiRepresentation {
PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo;
/** The host api implementation should populate the info field. In the
case of info.defaultInputDevice and info.defaultOutputDevice the
values stored should be 0 based indices within the host api's own
device index range (0 to deviceCount). These values will be converted
to global device indices by pa_front after PaUtilHostApiInitializer()
returns.
*/
PaHostApiInfo info;
PaDeviceInfo** deviceInfos;
/**
(*Terminate)() is guaranteed to be called with a valid <hostApi>
parameter, which was previously returned from the same implementation's
initializer.
*/
void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi );
/**
The inputParameters and outputParameters pointers should not be saved
as they will not remain valid after OpenStream is called.
The following guarantees are made about parameters to (*OpenStream)():
[NOTE: the following list up to *END PA FRONT VALIDATIONS* should be
kept in sync with the one for ValidateOpenStreamParameters and
Pa_OpenStream in pa_front.c]
PaHostApiRepresentation *hostApi
- is valid for this implementation
PaStream** stream
- is non-null
- at least one of inputParameters & outputParmeters is valid (not NULL)
- if inputParameters & outputParmeters are both valid, that
inputParameters->device & outputParmeters->device both use the same host api
PaDeviceIndex inputParameters->device
- is within range (0 to Pa_CountDevices-1) Or:
- is paUseHostApiSpecificDeviceSpecification and
inputParameters->hostApiSpecificStreamInfo is non-NULL and refers
to a valid host api
int inputParameters->numChannels
- if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0
- upper bound is NOT validated against device capabilities
PaSampleFormat inputParameters->sampleFormat
- is one of the sample formats defined in portaudio.h
void *inputParameters->hostApiSpecificStreamInfo
- if supplied its hostApi field matches the input device's host Api
PaDeviceIndex outputParmeters->device
- is within range (0 to Pa_CountDevices-1)
int outputParmeters->numChannels
- if inputDevice is valid, numInputChannels is > 0
- upper bound is NOT validated against device capabilities
PaSampleFormat outputParmeters->sampleFormat
- is one of the sample formats defined in portaudio.h
void *outputParmeters->hostApiSpecificStreamInfo
- if supplied its hostApi field matches the output device's host Api
double sampleRate
- is not an 'absurd' rate (less than 1000. or greater than 200000.)
- sampleRate is NOT validated against device capabilities
PaStreamFlags streamFlags
- unused platform neutral flags are zero
- paNeverDropInput is only used for full-duplex callback streams
with variable buffer size (paFramesPerBufferUnspecified)
[*END PA FRONT VALIDATIONS*]
The following validations MUST be performed by (*OpenStream)():
- check that input device can support numInputChannels
- check that input device can support inputSampleFormat, or that
we have the capability to convert from outputSampleFormat to
a native format
- if inputStreamInfo is supplied, validate its contents,
or return an error if no inputStreamInfo is expected
- check that output device can support numOutputChannels
- check that output device can support outputSampleFormat, or that
we have the capability to convert from outputSampleFormat to
a native format
- if outputStreamInfo is supplied, validate its contents,
or return an error if no outputStreamInfo is expected
- if a full duplex stream is requested, check that the combination
of input and output parameters is supported
- check that the device supports sampleRate
- alter sampleRate to a close allowable rate if necessary
- validate inputLatency and outputLatency
- validate any platform specific flags, if flags are supplied they
must be valid.
*/
PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi,
PaStream** stream,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate,
unsigned long framesPerCallback,
PaStreamFlags streamFlags,
PaStreamCallback *streamCallback,
void *userData );
PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
} PaUtilHostApiRepresentation;
/** Prototype for the initialization function which must be implemented by every
host API.
@see paHostApiInitializers
*/
typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex );
/** paHostApiInitializers is a NULL-terminated array of host API initialization
functions. These functions are called by pa_front to initialize the host APIs
when the client calls Pa_Initialize().
There is a platform specific file which defines paHostApiInitializers for that
platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example.
*/
extern PaUtilHostApiInitializer *paHostApiInitializers[];
/** The index of the default host API in the paHostApiInitializers array.
There is a platform specific file which defines paDefaultHostApiIndex for that
platform, see pa_win/pa_win_hostapis.c for example.
*/
extern int paDefaultHostApiIndex;
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_HOSTAPI_H */

View file

@ -1,806 +0,0 @@
/*
* $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, dev=%d, numChan=%d, format=%d, info=%p, /* input */ \n",
streamPtrPtr, inputDeviceID, numInputChannels,
inputSampleFormat, inputDriverInfo ));
DBUG((" dev=%d, numChan=%d, format=%d, info=%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;
}

1763
pa_common/pa_process.c Normal file

File diff suppressed because it is too large Load diff

741
pa_common/pa_process.h Normal file
View file

@ -0,0 +1,741 @@
#ifndef PA_PROCESS_H
#define PA_PROCESS_H
/*
* $Id$
* Portable Audio I/O Library callback buffer processing adapters
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Phil Burk, 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.
*/
/** @file
@brief Buffer Processor prototypes. A Buffer Processor performs buffer length
adaption, coordinates sample format conversion, and interleaves/deinterleaves
channels.
<h3>Overview</h3>
The "Buffer Processor" (PaUtilBufferProcessor) manages conversion of audio
data from host buffers to user buffers and back again. Where required, the
buffer processor takes care of converting between host and user sample formats,
interleaving and deinterleaving multichannel buffers, and adapting between host
and user buffers with different lengths. The buffer processor may be used with
full and half duplex streams, for both callback streams and blocking read/write
streams.
One of the important capabilities provided by the buffer processor is
the ability to adapt between user and host buffer sizes of different lengths
with minimum latency. Although this task is relatively easy to perform when
the host buffer size is an integer multiple of the user buffer size, the
problem is more complicated when this is not the case - especially for
full-duplex callback streams. Where necessary the adaption is implemented by
internally buffering some input and/or output data. The buffer adation
algorithm used by the buffer processor was originally implemented by
Stephan Letz for the ASIO version of PortAudio, and is described in his
Callback_adaption_.pdf which is included in the distribution.
The buffer processor performs sample conversion using the functions provided
by pa_converters.c.
The following sections provide an overview of how to use the buffer processor.
Interested readers are advised to consult the host API implementations for
examples of buffer processor usage.
<h4>Initialization, resetting and termination</h4>
When a stream is opened, the buffer processor should be initialized using
PaUtil_InitializeBufferProcessor. This function initializes internal state
and allocates temporary buffers as neccesary according to the supplied
configuration parameters. Some of the parameters correspond to those requested
by the user in their call to Pa_OpenStream(), others reflect the requirements
of the host API implementation - they indicate host buffer sizes, formats,
and the type of buffering which the Host API uses. The buffer processor should
be initialized for callback streams and blocking read/write streams.
Call PaUtil_ResetBufferProcessor to clear any sample data which is present
in the buffer processor before starting to use it (for example when
Pa_StartStream is called).
When the buffer processor is no longer used call
PaUtil_TerminateBufferProcessor.
<h4>Using the buffer processor for a callback stream</h4>
The buffer processor's role in a callback stream is to take host input buffers
process them with the stream callback, and fill host output buffers. For a
full duplex stream, the buffer processor handles input and output simultaneously
due to the requirements of the minimum-latency buffer adation algorithm.
When a host buffer becomes available, the implementation should call
the buffer processor to process the buffer. The buffer processor calls the
stream callback to consume and/or produce audio data as necessary. The buffer
processor will convert sample formats, interleave/deinterleave channels,
and slice or chunk the data to the appropriate buffer lengths according to
the requirements of the stream callback and the host API.
To process a host buffer (or a pair of host buffers for a full-duplex stream)
use the following calling sequence:
-# Call PaUtil_BeginBufferProcessing
-# For a stream which takes input:
- Call PaUtil_SetInputFrameCount with the number of frames in the host input
buffer.
- Call one of the following functions one or more times to tell the
buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
Which function you call will depend on whether the host buffer(s) are
interleaved or not.
- If the available host data is split accross two buffers (for example a
data range at the end of a circular buffer and another range at the
beginning of the circular buffer), also call
PaUtil_Set2ndInputFrameCount, PaUtil_Set2ndInputChannel,
PaUtil_Set2ndInterleavedInputChannels,
PaUtil_Set2ndNonInterleavedInputChannel as necessary to tell the buffer
processor about the second buffer.
-# For a stream which generates output:
- Call PaUtil_SetOutputFrameCount with the number of frames in the host
output buffer.
- Call one of the following functions one or more times to tell the
buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
Which function you call will depend on whether the host buffer(s) are
interleaved or not.
- If the available host output buffer space is split accross two buffers
(for example a data range at the end of a circular buffer and another
range at the beginning of the circular buffer), call
PaUtil_Set2ndOutputFrameCount, PaUtil_Set2ndOutputChannel,
PaUtil_Set2ndInterleavedOutputChannels,
PaUtil_Set2ndNonInterleavedOutputChannel as necessary to tell the buffer
processor about the second buffer.
-# Call PaUtil_EndBufferProcessing, this function performs the actual data
conversion and processing.
<h4>Using the buffer processor for a blocking read/write stream</h4>
Blocking read/write streams use the buffer processor to convert and copy user
output data to a host buffer, and to convert and copy host input data to
the user's buffer. The buffer processor does not perform any buffer adaption.
When using the buffer processor in a blocking read/write stream the input and
output conversion are performed separately by the PaUtil_CopyInput and
PaUtil_CopyOutput functions.
To copy data from a host input buffer to the buffer(s) which the user supplies
to Pa_ReadStream, use the following calling sequence.
- Repeat the following three steps until the user buffer(s) have been filled
with samples from the host input buffers:
-# Call PaUtil_SetInputFrameCount with the number of frames in the host
input buffer.
-# Call one of the following functions one or more times to tell the
buffer processor about the host input buffer(s): PaUtil_SetInputChannel,
PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel.
Which function you call will depend on whether the host buffer(s) are
interleaved or not.
-# Call PaUtil_CopyInput with the user buffer pointer (or a copy of the
array of buffer pointers for a non-interleaved stream) passed to
Pa_ReadStream, along with the number of frames in the user buffer(s).
Be careful to pass a <i>copy</i> of the user buffer pointers to
PaUtil_CopyInput because PaUtil_CopyInput advances the pointers to
the start of the next region to copy.
- PaUtil_CopyInput will not copy more data than is available in the
host buffer(s), so the above steps need to be repeated until the user
buffer(s) are full.
To copy data to the host output buffer from the user buffers(s) supplied
to Pa_WriteStream use the following calling sequence.
- Repeat the following three steps until all frames from the user buffer(s)
have been copied to the host API:
-# Call PaUtil_SetOutputFrameCount with the number of frames in the host
output buffer.
-# Call one of the following functions one or more times to tell the
buffer processor about the host output buffer(s): PaUtil_SetOutputChannel,
PaUtil_SetInterleavedOutputChannels, PaUtil_SetNonInterleavedOutputChannel.
Which function you call will depend on whether the host buffer(s) are
interleaved or not.
-# Call PaUtil_CopyOutput with the user buffer pointer (or a copy of the
array of buffer pointers for a non-interleaved stream) passed to
Pa_WriteStream, along with the number of frames in the user buffer(s).
Be careful to pass a <i>copy</i> of the user buffer pointers to
PaUtil_CopyOutput because PaUtil_CopyOutput advances the pointers to
the start of the next region to copy.
- PaUtil_CopyOutput will not copy more data than fits in the host buffer(s),
so the above steps need to be repeated until all user data is copied.
*/
#include "portaudio.h"
#include "pa_converters.h"
#include "pa_dither.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
/** @brief Mode flag passed to PaUtil_InitializeBufferProcessor indicating the type
of buffering that the host API uses.
The mode used depends on whether the host API or the implementation manages
the buffers, and how these buffers are used (scatter gather, circular buffer).
*/
typedef enum {
/** The host buffer size is a fixed known size. */
paUtilFixedHostBufferSize,
/** The host buffer size may vary, but has a known maximum size. */
paUtilBoundedHostBufferSize,
/** Nothing is known about the host buffer size. */
paUtilUnknownHostBufferSize,
/** The host buffer size varies, and the client does not require the buffer
processor to consume all of the input and fill all of the output buffer. This
is useful when the implementation has access to the host API's circular buffer
and only needs to consume/fill some of it, not necessarily all of it, with each
call to the buffer processor. This is the only mode where
PaUtil_EndBufferProcessing() may not consume the whole buffer.
*/
paUtilVariableHostBufferSizePartialUsageAllowed
}PaUtilHostBufferSizeMode;
/** @brief An auxilliary data structure used internally by the buffer processor
to represent host input and output buffers. */
typedef struct PaUtilChannelDescriptor{
void *data;
unsigned int stride; /**< stride in samples, not bytes */
}PaUtilChannelDescriptor;
/** @brief The main buffer processor data structure.
Allocate one of these, initialize it with PaUtil_InitializeBufferProcessor
and terminate it with PaUtil_TerminateBufferProcessor.
*/
typedef struct {
unsigned long framesPerUserBuffer;
unsigned long framesPerHostBuffer;
PaUtilHostBufferSizeMode hostBufferSizeMode;
int useNonAdaptingProcess;
unsigned long framesPerTempBuffer;
unsigned int inputChannelCount;
unsigned int bytesPerHostInputSample;
unsigned int bytesPerUserInputSample;
int userInputIsInterleaved;
PaUtilConverter *inputConverter;
PaUtilZeroer *inputZeroer;
unsigned int outputChannelCount;
unsigned int bytesPerHostOutputSample;
unsigned int bytesPerUserOutputSample;
int userOutputIsInterleaved;
PaUtilConverter *outputConverter;
PaUtilZeroer *outputZeroer;
unsigned long initialFramesInTempInputBuffer;
unsigned long initialFramesInTempOutputBuffer;
void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */
void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */
unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */
void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */
unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */
PaStreamCallbackTimeInfo *timeInfo;
PaStreamCallbackFlags callbackStatusFlags;
unsigned long hostInputFrameCount[2];
PaUtilChannelDescriptor *hostInputChannels[2]; /**< pointers to arrays of channel descriptors.
pointers are NULL for half-duplex output processing.
hostInputChannels[i].data is NULL when the caller
calls PaUtil_SetNoInput()
*/
unsigned long hostOutputFrameCount[2];
PaUtilChannelDescriptor *hostOutputChannels[2]; /**< pointers to arrays of channel descriptors.
pointers are NULL for half-duplex input processing.
hostOutputChannels[i].data is NULL when the caller
calls PaUtil_SetNoOutput()
*/
PaUtilTriangularDitherGenerator ditherGenerator;
double samplePeriod;
PaStreamCallback *streamCallback;
void *userData;
} PaUtilBufferProcessor;
/** @name Initialization, termination, resetting and info */
/*@{*/
/** Initialize a buffer processor's representation stored in a
PaUtilBufferProcessor structure. Be sure to call
PaUtil_TerminateBufferProcessor after finishing with a buffer processor.
@param bufferProcessor The buffer processor structure to initialize.
@param inputChannelCount The number of input channels as passed to
Pa_OpenStream or 0 for an output-only stream.
@param userInputSampleFormat Format of user input samples, as passed to
Pa_OpenStream. This parameter is ignored for ouput-only streams.
@param hostInputSampleFormat Format of host input samples. This parameter is
ignored for output-only streams. See note about host buffer interleave below.
@param outputChannelCount The number of output channels as passed to
Pa_OpenStream or 0 for an input-only stream.
@param userOutputSampleFormat Format of user output samples, as passed to
Pa_OpenStream. This parameter is ignored for input-only streams.
@param hostOutputSampleFormat Format of host output samples. This parameter is
ignored for input-only streams. See note about host buffer interleave below.
@param sampleRate Sample rate of the stream. The more accurate this is the
better - it is used for updating time stamps when adapting buffers.
@param streamFlags Stream flags as passed to Pa_OpenStream, this parameter is
used for selecting special sample conversion options such as clipping and
dithering.
@param framesPerUserBuffer Number of frames per user buffer, as requested
by the framesPerBuffer parameter to Pa_OpenStream. This parameter may be
zero to indicate that the user will accept any (and varying) buffer sizes.
@param framesPerHostBuffer Specifies the number of frames per host buffer
for the fixed buffer size mode, and the maximum number of frames
per host buffer for the bounded host buffer size mode. It is ignored for
the other modes.
@param hostBufferSizeMode A mode flag indicating the size variability of
host buffers that will be passed to the buffer processor. See
PaUtilHostBufferSizeMode for further details.
@param streamCallback The user stream callback passed to Pa_OpenStream.
@param userData The user data field passed to Pa_OpenStream.
@note The interleave flag is ignored for host buffer formats. Host
interleave is determined by the use of different SetInput and SetOutput
functions.
@return An error code indicating whether the initialization was successful.
If the error code is not PaNoError, the buffer processor was not initialized
and should not be used.
@see Pa_OpenStream, PaUtilHostBufferSizeMode, PaUtil_TerminateBufferProcessor
*/
PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor,
int inputChannelCount, PaSampleFormat userInputSampleFormat,
PaSampleFormat hostInputSampleFormat,
int outputChannelCount, PaSampleFormat userOutputSampleFormat,
PaSampleFormat hostOutputSampleFormat,
double sampleRate,
PaStreamFlags streamFlags,
unsigned long framesPerUserBuffer, /* 0 indicates don't care */
unsigned long framesPerHostBuffer,
PaUtilHostBufferSizeMode hostBufferSizeMode,
PaStreamCallback *streamCallback, void *userData );
/** Terminate a buffer processor's representation. Deallocates any temporary
buffers allocated by PaUtil_InitializeBufferProcessor.
@param bufferProcessor The buffer processor structure to terminate.
@see PaUtil_InitializeBufferProcessor.
*/
void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
/** Clear any internally buffered data. If you call
PaUtil_InitializeBufferProcessor in your OpenStream routine, make sure you
call PaUtil_ResetBufferProcessor in your StartStream call.
@param bufferProcessor The buffer processor to reset.
*/
void PaUtil_ResetBufferProcessor( PaUtilBufferProcessor* bufferProcessor );
/** Retrieve the input latency of a buffer processor.
@param bufferProcessor The buffer processor examine.
@return The input latency introduced by the buffer processor, in frames.
@see PaUtil_GetBufferProcessorOutputLatency
*/
unsigned long PaUtil_GetBufferProcessorInputLatency( PaUtilBufferProcessor* bufferProcessor );
/** Retrieve the output latency of a buffer processor.
@param bufferProcessor The buffer processor examine.
@return The output latency introduced by the buffer processor, in frames.
@see PaUtil_GetBufferProcessorInputLatency
*/
unsigned long PaUtil_GetBufferProcessorOutputLatency( PaUtilBufferProcessor* bufferProcessor );
/*@}*/
/** @name Host buffer pointer configuration
Functions to set host input and output buffers, used by both callback streams
and blocking read/write streams.
*/
/*@{*/
/** Set the number of frames in the input host buffer(s) specified by the
PaUtil_Set*InputChannel functions.
@param bufferProcessor The buffer processor.
@param frameCount The number of host input frames. A 0 frameCount indicates to
use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
@see PaUtil_SetNoInput, PaUtil_SetInputChannel,
PaUtil_SetInterleavedInputChannels, PaUtil_SetNonInterleavedInputChannel
*/
void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
unsigned long frameCount );
/** Indicate that no input is avalable. This function should be used when
priming the output of a full-duplex stream opened with the
paPrimeOutputBuffersUsingStreamCallback flag. Note that it is not necessary
to call this or any othe PaUtil_Set*Input* functions for ouput-only streams.
@param bufferProcessor The buffer processor.
*/
void PaUtil_SetNoInput( PaUtilBufferProcessor* bufferProcessor );
/** Provide the buffer processor with a pointer to a host input channel.
@param bufferProcessor The buffer processor.
@param channel The channel number.
@param data The buffer.
@param stride The stride from one sample to the next, in samples. For
interleaved host buffers, the stride will usually be the same as the number of
channels in the buffer.
*/
void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor,
unsigned int channel, void *data, unsigned int stride );
/** Provide the buffer processor with a pointer to an number of interleaved
host input channels.
@param bufferProcessor The buffer processor.
@param firstChannel The first channel number.
@param data The buffer.
@param channelCount The number of interleaved channels in the buffer. If
channelCount is zero, the number of channels specified to
PaUtil_InitializeBufferProcessor will be used.
*/
void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
unsigned int firstChannel, void *data, unsigned int channelCount );
/** Provide the buffer processor with a pointer to one non-interleaved host
output channel.
@param bufferProcessor The buffer processor.
@param channel The channel number.
@param data The buffer.
*/
void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
unsigned int channel, void *data );
/** Use for the second buffer half when the input buffer is split in two halves.
@see PaUtil_SetInputFrameCount
*/
void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor,
unsigned long frameCount );
/** Use for the second buffer half when the input buffer is split in two halves.
@see PaUtil_SetInputChannel
*/
void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor,
unsigned int channel, void *data, unsigned int stride );
/** Use for the second buffer half when the input buffer is split in two halves.
@see PaUtil_SetInterleavedInputChannels
*/
void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor,
unsigned int firstChannel, void *data, unsigned int channelCount );
/** Use for the second buffer half when the input buffer is split in two halves.
@see PaUtil_SetNonInterleavedInputChannel
*/
void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor,
unsigned int channel, void *data );
/** Set the number of frames in the output host buffer(s) specified by the
PaUtil_Set*OutputChannel functions.
@param bufferProcessor The buffer processor.
@param frameCount The number of host output frames. A 0 frameCount indicates to
use the framesPerHostBuffer value passed to PaUtil_InitializeBufferProcessor.
@see PaUtil_SetOutputChannel, PaUtil_SetInterleavedOutputChannels,
PaUtil_SetNonInterleavedOutputChannel
*/
void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
unsigned long frameCount );
/** Indicate that the output will be discarded. This function should be used
when implementing the paNeverDropInput mode for full duplex streams.
@param bufferProcessor The buffer processor.
*/
void PaUtil_SetNoOutput( PaUtilBufferProcessor* bufferProcessor );
/** Provide the buffer processor with a pointer to a host output channel.
@param bufferProcessor The buffer processor.
@param channel The channel number.
@param data The buffer.
@param stride The stride from one sample to the next, in samples. For
interleaved host buffers, the stride will usually be the same as the number of
channels in the buffer.
*/
void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor,
unsigned int channel, void *data, unsigned int stride );
/** Provide the buffer processor with a pointer to a number of interleaved
host output channels.
@param bufferProcessor The buffer processor.
@param firstChannel The first channel number.
@param data The buffer.
@param channelCount The number of interleaved channels in the buffer. If
channelCount is zero, the number of channels specified to
PaUtil_InitializeBufferProcessor will be used.
*/
void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
unsigned int firstChannel, void *data, unsigned int channelCount );
/** Provide the buffer processor with a pointer to one non-interleaved host
output channel.
@param bufferProcessor The buffer processor.
@param channel The channel number.
@param data The buffer.
*/
void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
unsigned int channel, void *data );
/** Use for the second buffer half when the output buffer is split in two halves.
@see PaUtil_SetOutputFrameCount
*/
void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor,
unsigned long frameCount );
/** Use for the second buffer half when the output buffer is split in two halves.
@see PaUtil_SetOutputChannel
*/
void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor,
unsigned int channel, void *data, unsigned int stride );
/** Use for the second buffer half when the output buffer is split in two halves.
@see PaUtil_SetInterleavedOutputChannels
*/
void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor,
unsigned int firstChannel, void *data, unsigned int channelCount );
/** Use for the second buffer half when the output buffer is split in two halves.
@see PaUtil_SetNonInterleavedOutputChannel
*/
void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor,
unsigned int channel, void *data );
/*@}*/
/** @name Buffer processing functions for callback streams
*/
/*@{*/
/** Commence processing a host buffer (or a pair of host buffers in the
full-duplex case) for a callback stream.
@param bufferProcessor The buffer processor.
@param timeInfo Timing information for the first sample of the host
buffer(s). This information may be adjusted when buffer adaption is being
performed.
@param callbackStatusFlags Flags indicating whether underruns and overruns
have occurred since the last time the buffer processor was called.
*/
void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags callbackStatusFlags );
/** Finish processing a host buffer (or a pair of host buffers in the
full-duplex case) for a callback stream.
@param bufferProcessor The buffer processor.
@param callbackResult On input, indicates a previous callback result, and on
exit, the result of the user stream callback, if it is called.
On entry callbackResult should contain one of { paContinue, paComplete, or
paAbort}. If paComplete is passed, the stream callback will not be called
but any audio that was generated by previous stream callbacks will be copied
to the output buffer(s). You can check whether the buffer processor's internal
buffer is empty by calling PaUtil_IsBufferProcessorOutputEmpty.
If the stream callback is called its result is stored in *callbackResult. If
the stream callback returns paComplete or paAbort, all output buffers will be
full of valid data - some of which may be zeros to acount for data that
wasn't generated by the terminating callback.
@return The number of frames processed. This usually corresponds to the
number of frames specified by the PaUtil_Set*FrameCount functions, exept in
the paUtilVariableHostBufferSizePartialUsageAllowed buffer size mode when a
smaller value may be returned.
*/
unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor,
int *callbackResult );
/** Determine whether any callback generated output remains in the bufffer
processor's internal buffers. This method may be used to determine when to
continue calling PaUtil_EndBufferProcessing() after the callback has returned
a callbackResult of paComplete.
@param bufferProcessor The buffer processor.
@return Returns non-zero when callback generated output remains in the internal
buffer and zero (0) when there internal buffer contains no callback generated
data.
*/
int PaUtil_IsBufferProcessorOutputEmpty( PaUtilBufferProcessor* bufferProcessor );
/*@}*/
/** @name Buffer processing functions for blocking read/write streams
*/
/*@{*/
/** Copy samples from host input channels set up by the PaUtil_Set*InputChannels
functions to a user supplied buffer. This function is intended for use with
blocking read/write streams. Copies the minimum of the number of
user frames (specified by the frameCount parameter) and the number of available
host frames (specified in a previous call to SetInputFrameCount()).
@param bufferProcessor The buffer processor.
@param buffer A pointer to the user buffer pointer, or a pointer to a pointer
to an array of user buffer pointers for a non-interleaved stream. It is
important that this parameter points to a copy of the user buffer pointers,
not to the actual user buffer pointers, because this function updates the
pointers before returning.
@param frameCount The number of frames of data in the buffer(s) pointed to by
the buffer parameter.
@return The number of frames copied. The buffer pointer(s) pointed to by the
buffer parameter are advanced to point to the frame(s) following the last one
filled.
*/
unsigned long PaUtil_CopyInput( PaUtilBufferProcessor* bufferProcessor,
void **buffer, unsigned long frameCount );
/* Copy samples from a user supplied buffer to host output channels set up by
the PaUtil_Set*OutputChannels functions. This function is intended for use with
blocking read/write streams. Copies the minimum of the number of
user frames (specified by the frameCount parameter) and the number of
host frames (specified in a previous call to SetOutputFrameCount()).
@param bufferProcessor The buffer processor.
@param buffer A pointer to the user buffer pointer, or a pointer to a pointer
to an array of user buffer pointers for a non-interleaved stream. It is
important that this parameter points to a copy of the user buffer pointers,
not to the actual user buffer pointers, because this function updates the
pointers before returning.
@param frameCount The number of frames of data in the buffer(s) pointed to by
the buffer parameter.
@return The number of frames copied. The buffer pointer(s) pointed to by the
buffer parameter are advanced to point to the frame(s) following the last one
copied.
*/
unsigned long PaUtil_CopyOutput( PaUtilBufferProcessor* bufferProcessor,
const void ** buffer, unsigned long frameCount );
/* Zero samples in host output channels set up by the PaUtil_Set*OutputChannels
functions. This function is useful for flushing streams.
Zeros the minimum of frameCount and the number of host frames specified in a
previous call to SetOutputFrameCount().
@param bufferProcessor The buffer processor.
@param frameCount The maximum number of frames to zero.
@return The number of frames zeroed.
*/
unsigned long PaUtil_ZeroOutput( PaUtilBufferProcessor* bufferProcessor,
unsigned long frameCount );
/*@}*/
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_PROCESS_H */

807
pa_common/pa_skeleton.c Normal file
View file

@ -0,0 +1,807 @@
/*
* $Id$
* Portable Audio I/O Library skeleton implementation
* demonstrates how to use the common functions to implement support
* for a host API
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, 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.
*/
/** @file
@brief Skeleton implementation of support for a host API.
@note This file is provided as a starting point for implementing support for
a new host API. IMPLEMENT ME comments are used to indicate functionality
which much be customised for each implementation.
*/
#include <string.h> /* strlen() */
#include "pa_util.h"
#include "pa_allocation.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
/* prototypes for functions declared in this file */
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
#ifdef __cplusplus
}
#endif /* __cplusplus */
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate,
unsigned long framesPerBuffer,
PaStreamFlags streamFlags,
PaStreamCallback *streamCallback,
void *userData );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
static PaError AbortStream( PaStream *stream );
static PaError IsStreamStopped( PaStream *s );
static PaError IsStreamActive( PaStream *stream );
static PaTime GetStreamTime( PaStream *stream );
static double GetStreamCpuLoad( PaStream* stream );
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
static signed long GetStreamReadAvailable( PaStream* stream );
static signed long GetStreamWriteAvailable( PaStream* stream );
/* IMPLEMENT ME: a macro like the following one should be used for reporting
host errors */
#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \
PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */
typedef struct
{
PaUtilHostApiRepresentation inheritedHostApiRep;
PaUtilStreamInterface callbackStreamInterface;
PaUtilStreamInterface blockingStreamInterface;
PaUtilAllocationGroup *allocations;
/* implementation specific data goes here */
}
PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */
PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
int i, deviceCount;
PaSkeletonHostApiRepresentation *skeletonHostApi;
PaDeviceInfo *deviceInfoArray;
skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) );
if( !skeletonHostApi )
{
result = paInsufficientMemory;
goto error;
}
skeletonHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !skeletonHostApi->allocations )
{
result = paInsufficientMemory;
goto error;
}
*hostApi = &skeletonHostApi->inheritedHostApiRep;
(*hostApi)->info.structVersion = 1;
(*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */
(*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */
(*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */
(*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */
(*hostApi)->info.deviceCount = 0;
deviceCount = 0; /* IMPLEMENT ME */
if( deviceCount > 0 )
{
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
if( !(*hostApi)->deviceInfos )
{
result = paInsufficientMemory;
goto error;
}
/* allocate all device info structs in a contiguous block */
deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory(
skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
for( i=0; i < deviceCount; ++i )
{
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg:
deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 );
if( !deviceName )
{
result = paInsufficientMemory;
goto error;
}
strcpy( deviceName, srcName );
deviceInfo->name = deviceName;
*/
deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */
deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */
deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */
deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */
deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */
deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */
deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */
(*hostApi)->deviceInfos[i] = deviceInfo;
++(*hostApi)->info.deviceCount;
}
}
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
GetStreamTime, GetStreamCpuLoad,
PaUtil_DummyRead, PaUtil_DummyWrite,
PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
GetStreamTime, PaUtil_DummyGetCpuLoad,
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
return result;
error:
if( skeletonHostApi )
{
if( skeletonHostApi->allocations )
{
PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
}
PaUtil_FreeMemory( skeletonHostApi );
}
return result;
}
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
/*
IMPLEMENT ME:
- clean up any resources not handled by the allocation group
*/
if( skeletonHostApi->allocations )
{
PaUtil_FreeAllAllocations( skeletonHostApi->allocations );
PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations );
}
PaUtil_FreeMemory( skeletonHostApi );
}
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate )
{
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
if( inputParameters )
{
inputChannelCount = inputParameters->channelCount;
inputSampleFormat = inputParameters->sampleFormat;
/* all standard sample formats are supported by the buffer adapter,
this implementation doesn't support any custom sample formats */
if( inputSampleFormat & paCustomFormat )
return paSampleFormatNotSupported;
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
return paInvalidDevice;
/* check that input device can support inputChannelCount */
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
return paInvalidChannelCount;
/* validate inputStreamInfo */
if( inputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
}
else
{
inputChannelCount = 0;
}
if( outputParameters )
{
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
/* all standard sample formats are supported by the buffer adapter,
this implementation doesn't support any custom sample formats */
if( outputSampleFormat & paCustomFormat )
return paSampleFormatNotSupported;
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
return paInvalidDevice;
/* check that output device can support outputChannelCount */
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
return paInvalidChannelCount;
/* validate outputStreamInfo */
if( outputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
}
else
{
outputChannelCount = 0;
}
/*
IMPLEMENT ME:
- if a full duplex stream is requested, check that the combination
of input and output parameters is supported if necessary
- check that the device supports sampleRate
Because the buffer adapter handles conversion between all standard
sample formats, the following checks are only required if paCustomFormat
is implemented, or under some other unusual conditions.
- check that input device can support inputSampleFormat, or that
we have the capability to convert from inputSampleFormat to
a native format
- check that output device can support outputSampleFormat, or that
we have the capability to convert from outputSampleFormat to
a native format
*/
/* suppress unused variable warnings */
(void) sampleRate;
return paFormatIsSupported;
}
/* PaSkeletonStream - a stream data structure specifically for this implementation */
typedef struct PaSkeletonStream
{ /* IMPLEMENT ME: rename this */
PaUtilStreamRepresentation streamRepresentation;
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
PaUtilBufferProcessor bufferProcessor;
/* IMPLEMENT ME:
- implementation specific data goes here
*/
unsigned long framesPerHostCallback; /* just an example */
}
PaSkeletonStream;
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate,
unsigned long framesPerBuffer,
PaStreamFlags streamFlags,
PaStreamCallback *streamCallback,
void *userData )
{
PaError result = paNoError;
PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi;
PaSkeletonStream *stream = 0;
unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
if( inputParameters )
{
inputChannelCount = inputParameters->channelCount;
inputSampleFormat = inputParameters->sampleFormat;
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
return paInvalidDevice;
/* check that input device can support inputChannelCount */
if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
return paInvalidChannelCount;
/* validate inputStreamInfo */
if( inputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
/* IMPLEMENT ME - establish which host formats are available */
hostInputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
}
else
{
inputChannelCount = 0;
inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
}
if( outputParameters )
{
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
return paInvalidDevice;
/* check that output device can support inputChannelCount */
if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
return paInvalidChannelCount;
/* validate outputStreamInfo */
if( outputParameters->hostApiSpecificStreamInfo )
return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
/* IMPLEMENT ME - establish which host formats are available */
hostOutputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
}
else
{
outputChannelCount = 0;
outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
}
/*
IMPLEMENT ME:
( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? )
- check that input device can support inputSampleFormat, or that
we have the capability to convert from outputSampleFormat to
a native format
- check that output device can support outputSampleFormat, or that
we have the capability to convert from outputSampleFormat to
a native format
- if a full duplex stream is requested, check that the combination
of input and output parameters is supported
- check that the device supports sampleRate
- alter sampleRate to a close allowable rate if possible / necessary
- validate suggestedInputLatency and suggestedOutputLatency parameters,
use default values where necessary
*/
/* validate platform specific flags */
if( (streamFlags & paPlatformSpecificFlags) != 0 )
return paInvalidFlag; /* unexpected platform specific flag */
stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) );
if( !stream )
{
result = paInsufficientMemory;
goto error;
}
if( streamCallback )
{
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
&skeletonHostApi->callbackStreamInterface, streamCallback, userData );
}
else
{
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
&skeletonHostApi->blockingStreamInterface, streamCallback, userData );
}
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
/* we assume a fixed host buffer size in this example, but the buffer processor
can also support bounded and unknown host buffer sizes by passing
paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of
paUtilFixedHostBufferSize below. */
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
inputChannelCount, inputSampleFormat, hostInputSampleFormat,
outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
sampleRate, streamFlags, framesPerBuffer,
framesPerHostBuffer, paUtilFixedHostBufferSize,
streamCallback, userData );
if( result != paNoError )
goto error;
/*
IMPLEMENT ME: initialise the following fields with estimated or actual
values.
*/
stream->streamRepresentation.streamInfo.inputLatency =
PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor);
stream->streamRepresentation.streamInfo.outputLatency =
PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor);
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
/*
IMPLEMENT ME:
- additional stream setup + opening
*/
stream->framesPerHostCallback = framesPerHostBuffer;
*s = (PaStream*)stream;
return result;
error:
if( stream )
PaUtil_FreeMemory( stream );
return result;
}
/*
ExampleHostProcessingLoop() illustrates the kind of processing which may
occur in a host implementation.
*/
static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData )
{
PaSkeletonStream *stream = (PaSkeletonStream*)userData;
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */
int callbackResult;
unsigned long framesProcessed;
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
/*
IMPLEMENT ME:
- generate timing information
- handle buffer slips
*/
/*
If you need to byte swap or shift inputBuffer to convert it into a
portaudio format, do it here.
*/
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, 0 /* IMPLEMENT ME: pass underflow/overflow flags when necessary */ );
/*
depending on whether the host buffers are interleaved, non-interleaved
or a mixture, you will want to call PaUtil_SetInterleaved*Channels(),
PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here.
*/
PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor,
0, /* first channel of inputBuffer is channel 0 */
inputBuffer,
0 ); /* 0 - use inputChannelCount passed to init buffer processor */
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor,
0, /* first channel of outputBuffer is channel 0 */
outputBuffer,
0 ); /* 0 - use outputChannelCount passed to init buffer processor */
/* you must pass a valid value of callback result to PaUtil_EndBufferProcessing()
in general you would pass paContinue for normal operation, and
paComplete to drain the buffer processor's internal output buffer.
You can check whether the buffer processor's output buffer is empty
using PaUtil_IsBufferProcessorOuputEmpty( bufferProcessor )
*/
callbackResult = paContinue;
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
/*
If you need to byte swap or shift outputBuffer to convert it to
host format, do it here.
*/
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
if( callbackResult == paContinue )
{
/* nothing special to do */
}
else if( callbackResult == paAbort )
{
/* IMPLEMENT ME - finish playback immediately */
/* once finished, call the finished callback */
if( stream->streamRepresentation.streamFinishedCallback != 0 )
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
}
else
{
/* User callback has asked us to stop with paComplete or other non-zero value */
/* IMPLEMENT ME - finish playback once currently queued audio has completed */
/* once finished, call the finished callback */
if( stream->streamRepresentation.streamFinishedCallback != 0 )
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
}
}
/*
When CloseStream() is called, the multi-api layer ensures that
the stream has already been stopped or aborted.
*/
static PaError CloseStream( PaStream* s )
{
PaError result = paNoError;
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/*
IMPLEMENT ME:
- additional stream closing + cleanup
*/
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
PaUtil_FreeMemory( stream );
return result;
}
static PaError StartStream( PaStream *s )
{
PaError result = paNoError;
PaSkeletonStream *stream = (PaSkeletonStream*)s;
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
/* IMPLEMENT ME, see portaudio.h for required behavior */
/* suppress unused function warning. the code in ExampleHostProcessingLoop or
something similar should be implemented to feed samples to and from the
host after StartStream() is called.
*/
(void) ExampleHostProcessingLoop;
return result;
}
static PaError StopStream( PaStream *s )
{
PaError result = paNoError;
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior */
return result;
}
static PaError AbortStream( PaStream *s )
{
PaError result = paNoError;
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior */
return result;
}
static PaError IsStreamStopped( PaStream *s )
{
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior */
return 0;
}
static PaError IsStreamActive( PaStream *s )
{
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior */
return 0;
}
static PaTime GetStreamTime( PaStream *s )
{
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
return 0;
}
static double GetStreamCpuLoad( PaStream* s )
{
PaSkeletonStream *stream = (PaSkeletonStream*)s;
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
}
/*
As separate stream interfaces are used for blocking and callback
streams, the following functions can be guaranteed to only be called
for blocking streams.
*/
static PaError ReadStream( PaStream* s,
void *buffer,
unsigned long frames )
{
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) buffer;
(void) frames;
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
return paNoError;
}
static PaError WriteStream( PaStream* s,
const void *buffer,
unsigned long frames )
{
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) buffer;
(void) frames;
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
return paNoError;
}
static signed long GetStreamReadAvailable( PaStream* s )
{
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
return 0;
}
static signed long GetStreamWriteAvailable( PaStream* s )
{
PaSkeletonStream *stream = (PaSkeletonStream*)s;
/* suppress unused variable warnings */
(void) stream;
/* IMPLEMENT ME, see portaudio.h for required behavior*/
return 0;
}

141
pa_common/pa_stream.c Normal file
View file

@ -0,0 +1,141 @@
/*
* $Id$
* Portable Audio I/O Library
*
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 2002 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.
*/
/** @file
@brief Interface used by pa_front to virtualize functions which operate on
streams.
*/
#include "pa_stream.h"
void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
PaError (*Close)( PaStream* ),
PaError (*Start)( PaStream* ),
PaError (*Stop)( PaStream* ),
PaError (*Abort)( PaStream* ),
PaError (*IsStopped)( PaStream* ),
PaError (*IsActive)( PaStream* ),
PaTime (*GetTime)( PaStream* ),
double (*GetCpuLoad)( PaStream* ),
PaError (*Read)( PaStream*, void *, unsigned long ),
PaError (*Write)( PaStream*, const void *, unsigned long ),
signed long (*GetReadAvailable)( PaStream* ),
signed long (*GetWriteAvailable)( PaStream* ) )
{
streamInterface->Close = Close;
streamInterface->Start = Start;
streamInterface->Stop = Stop;
streamInterface->Abort = Abort;
streamInterface->IsStopped = IsStopped;
streamInterface->IsActive = IsActive;
streamInterface->GetTime = GetTime;
streamInterface->GetCpuLoad = GetCpuLoad;
streamInterface->Read = Read;
streamInterface->Write = Write;
streamInterface->GetReadAvailable = GetReadAvailable;
streamInterface->GetWriteAvailable = GetWriteAvailable;
}
void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation,
PaUtilStreamInterface *streamInterface,
PaStreamCallback *streamCallback,
void *userData )
{
streamRepresentation->magic = PA_STREAM_MAGIC;
streamRepresentation->nextOpenStream = 0;
streamRepresentation->streamInterface = streamInterface;
streamRepresentation->streamCallback = streamCallback;
streamRepresentation->streamFinishedCallback = 0;
streamRepresentation->userData = userData;
streamRepresentation->streamInfo.inputLatency = 0.;
streamRepresentation->streamInfo.outputLatency = 0.;
streamRepresentation->streamInfo.sampleRate = 0.;
}
void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation )
{
streamRepresentation->magic = 0;
}
PaError PaUtil_DummyRead( PaStream* stream,
void *buffer,
unsigned long frames )
{
(void)stream; /* unused parameter */
(void)buffer; /* unused parameter */
(void)frames; /* unused parameter */
return paCanNotReadFromACallbackStream;
}
PaError PaUtil_DummyWrite( PaStream* stream,
const void *buffer,
unsigned long frames )
{
(void)stream; /* unused parameter */
(void)buffer; /* unused parameter */
(void)frames; /* unused parameter */
return paCanNotWriteToACallbackStream;
}
signed long PaUtil_DummyGetReadAvailable( PaStream* stream )
{
(void)stream; /* unused parameter */
return paCanNotReadFromACallbackStream;
}
signed long PaUtil_DummyGetWriteAvailable( PaStream* stream )
{
(void)stream; /* unused parameter */
return paCanNotWriteToACallbackStream;
}
double PaUtil_DummyGetCpuLoad( PaStream* stream )
{
(void)stream; /* unused parameter */
return 0.0;
}

196
pa_common/pa_stream.h Normal file
View file

@ -0,0 +1,196 @@
#ifndef PA_STREAM_H
#define PA_STREAM_H
/*
* $Id$
* Portable Audio I/O Library
* stream interface
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, 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.
*/
/** @file
@brief Interface used by pa_front to virtualize functions which operate on
streams.
*/
#include "portaudio.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
#define PA_STREAM_MAGIC (0x18273645)
/** A structure representing an (abstract) interface to a host API. Contains
pointers to functions which implement the interface.
All PaStreamInterface functions are guaranteed to be called with a non-null,
valid stream parameter.
*/
typedef struct {
PaError (*Close)( PaStream* stream );
PaError (*Start)( PaStream *stream );
PaError (*Stop)( PaStream *stream );
PaError (*Abort)( PaStream *stream );
PaError (*IsStopped)( PaStream *stream );
PaError (*IsActive)( PaStream *stream );
PaTime (*GetTime)( PaStream *stream );
double (*GetCpuLoad)( PaStream* stream );
PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames );
PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames );
signed long (*GetReadAvailable)( PaStream* stream );
signed long (*GetWriteAvailable)( PaStream* stream );
} PaUtilStreamInterface;
/** Initialize the fields of a PaUtilStreamInterface structure.
*/
void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface,
PaError (*Close)( PaStream* ),
PaError (*Start)( PaStream* ),
PaError (*Stop)( PaStream* ),
PaError (*Abort)( PaStream* ),
PaError (*IsStopped)( PaStream* ),
PaError (*IsActive)( PaStream* ),
PaTime (*GetTime)( PaStream* ),
double (*GetCpuLoad)( PaStream* ),
PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ),
PaError (*Write)( PaStream* stream, const void *buffer, unsigned long frames ),
signed long (*GetReadAvailable)( PaStream* stream ),
signed long (*GetWriteAvailable)( PaStream* stream ) );
/** Dummy Read function for use in interfaces to a callback based streams.
Pass to the Read parameter of PaUtil_InitializeStreamInterface.
@return An error code indicating that the function has no effect
because the stream is a callback stream.
*/
PaError PaUtil_DummyRead( PaStream* stream,
void *buffer,
unsigned long frames );
/** Dummy Write function for use in an interfaces to callback based streams.
Pass to the Write parameter of PaUtil_InitializeStreamInterface.
@return An error code indicating that the function has no effect
because the stream is a callback stream.
*/
PaError PaUtil_DummyWrite( PaStream* stream,
const void *buffer,
unsigned long frames );
/** Dummy GetReadAvailable function for use in interfaces to callback based
streams. Pass to the GetReadAvailable parameter of PaUtil_InitializeStreamInterface.
@return An error code indicating that the function has no effect
because the stream is a callback stream.
*/
signed long PaUtil_DummyGetReadAvailable( PaStream* stream );
/** Dummy GetWriteAvailable function for use in interfaces to callback based
streams. Pass to the GetWriteAvailable parameter of PaUtil_InitializeStreamInterface.
@return An error code indicating that the function has no effect
because the stream is a callback stream.
*/
signed long PaUtil_DummyGetWriteAvailable( PaStream* stream );
/** Dummy GetCpuLoad function for use in an interface to a read/write stream.
Pass to the GetCpuLoad parameter of PaUtil_InitializeStreamInterface.
@return Returns 0.
*/
double PaUtil_DummyGetCpuLoad( PaStream* stream );
/** Non host specific data for a stream. This data is used by pa_front to
forward to the appropriate functions in the streamInterface structure.
*/
typedef struct PaUtilStreamRepresentation {
unsigned long magic; /**< set to PA_STREAM_MAGIC */
struct PaUtilStreamRepresentation *nextOpenStream; /**< field used by multi-api code */
PaUtilStreamInterface *streamInterface;
PaStreamCallback *streamCallback;
PaStreamFinishedCallback *streamFinishedCallback;
void *userData;
PaStreamInfo streamInfo;
} PaUtilStreamRepresentation;
/** Initialize a PaUtilStreamRepresentation structure.
@see PaUtil_InitializeStreamRepresentation
*/
void PaUtil_InitializeStreamRepresentation(
PaUtilStreamRepresentation *streamRepresentation,
PaUtilStreamInterface *streamInterface,
PaStreamCallback *streamCallback,
void *userData );
/** Clean up a PaUtilStreamRepresentation structure previously initialized
by a call to PaUtil_InitializeStreamRepresentation.
@see PaUtil_InitializeStreamRepresentation
*/
void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation );
/** Check that the stream pointer is valid.
@return Returns paNoError if the stream pointer appears to be OK, otherwise
returns an error indicating the cause of failure.
*/
PaError PaUtil_ValidateStreamPointer( PaStream *stream );
/** Cast an opaque stream pointer into a pointer to a PaUtilStreamRepresentation.
@see PaUtilStreamRepresentation
*/
#define PA_STREAM_REP( stream )\
((PaUtilStreamRepresentation*) (stream) )
/** Cast an opaque stream pointer into a pointer to a PaUtilStreamInterface.
@see PaUtilStreamRepresentation, PaUtilStreamInterface
*/
#define PA_STREAM_INTERFACE( stream )\
PA_STREAM_REP( (stream) )->streamInterface
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_STREAM_H */

View file

@ -30,49 +30,54 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/** @file
@brief Event trace mechanism for debugging.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pa_trace.h"
#if TRACE_REALTIME_EVENTS
#if PA_TRACE_REALTIME_EVENTS
static char *traceTextArray[MAX_TRACE_RECORDS];
static int traceIntArray[MAX_TRACE_RECORDS];
static char *traceTextArray[PA_MAX_TRACE_RECORDS];
static int traceIntArray[PA_MAX_TRACE_RECORDS];
static int traceIndex = 0;
static int traceBlock = 0;
/*********************************************************************/
void ResetTraceMessages()
void PaUtil_ResetTraceMessages()
{
traceIndex = 0;
}
/*********************************************************************/
void DumpTraceMessages()
void PaUtil_DumpTraceMessages()
{
int i;
int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS;
int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS;
printf("DumpTraceMessages: traceIndex = %d\n", traceIndex );
for( i=0; i<numDump; i++ )
for( i=0; i<messageCount; i++ )
{
printf("%3d: %s = 0x%08X\n",
i, traceTextArray[i], traceIntArray[i] );
}
ResetTraceMessages();
PaUtil_ResetTraceMessages();
fflush(stdout);
}
/*********************************************************************/
void AddTraceMessage( char *msg, int data )
void PaUtil_AddTraceMessage( const char *msg, int data )
{
if( (traceIndex == MAX_TRACE_RECORDS) && (traceBlock == 0) )
if( (traceIndex == PA_MAX_TRACE_RECORDS) && (traceBlock == 0) )
{
traceBlock = 1;
/* DumpTraceMessages(); */
/* PaUtil_DumpTraceMessages(); */
}
else if( traceIndex < MAX_TRACE_RECORDS )
else if( traceIndex < PA_MAX_TRACE_RECORDS )
{
traceTextArray[traceIndex] = msg;
traceIntArray[traceIndex] = data;
@ -80,4 +85,4 @@ void AddTraceMessage( char *msg, int data )
}
}
#endif
#endif /* TRACE_REALTIME_EVENTS */

View file

@ -32,9 +32,15 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/** @file
@brief Event trace mechanism for debugging.
#define TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
#define MAX_TRACE_RECORDS (2048)
Allows data to be written to the buffer at interrupt time and dumped later.
*/
#define PA_TRACE_REALTIME_EVENTS (0) /* Keep log of various real-time events. */
#define PA_MAX_TRACE_RECORDS (2048)
#ifdef __cplusplus
extern "C"
@ -42,24 +48,21 @@ extern "C"
#endif /* __cplusplus */
/************************************************************************************/
/****************** Prototypes ******************************************************/
/************************************************************************************/
#if TRACE_REALTIME_EVENTS
void DumpTraceMessages();
void ResetTraceMessages();
void AddTraceMessage( char *msg, int data );
#if PA_TRACE_REALTIME_EVENTS
void PaUtil_ResetTraceMessages();
void PaUtil_AddTraceMessage( const char *msg, int data );
void PaUtil_DumpTraceMessages();
#else
#define AddTraceMessage(msg,data) /* noop */
#define ResetTraceMessages() /* noop */
#define DumpTraceMessages() /* noop */
#define PaUtil_ResetTraceMessages() /* noop */
#define PaUtil_AddTraceMessage(msg,data) /* noop */
#define PaUtil_DumpTraceMessages() /* noop */
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */

65
pa_common/pa_types.h Normal file
View file

@ -0,0 +1,65 @@
#ifndef PA_TYPES_H
#define PA_TYPES_H
/*
SIZEOF_SHORT, SIZEOF_INT and SIZEOF_LONG are set by the configure script
when it is used. Otherwise we default to the common 32 bit values, if your
platform doesn't use configure, and doesn't use the default values below
you will need to explicitly define these symbols in your make file.
A PA_VALIDATE_SIZES macro is provided to assert that the values set in this
file are correct.
*/
#ifndef SIZEOF_SHORT
#define SIZEOF_SHORT 2
#endif
#ifndef SIZEOF_INT
#define SIZEOF_INT 4
#endif
#ifndef SIZEOF_LONG
#define SIZEOF_LONG 4
#endif
#if SIZEOF_SHORT == 2
typedef signed short PaInt16;
typedef unsigned short PaUint16;
#elif SIZEOF_INT == 2
typedef signed int PaInt16;
typedef unsigned int PaUint16;
#else
#error pa_types.h was unable to determine which type to use for 16bit integers on the target platform
#endif
#if SIZEOF_SHORT == 4
typedef signed short PaInt32;
typedef unsigned short PaUint32;
#elif SIZEOF_INT == 4
typedef signed int PaInt32;
typedef unsigned int PaUint32;
#elif SIZEOF_LONG == 4
typedef signed long PaInt32;
typedef unsigned long PaUint32;
#else
#error pa_types.h was unable to determine which type to use for 32bit integers on the target platform
#endif
/* PA_VALIDATE_TYPE_SIZES compares the size of the integer types at runtime to
ensure that PortAudio was configured correctly, and raises an assertion if
they don't match the expected values. <assert.h> must be included in the
context in which this macro is used.
*/
#define PA_VALIDATE_TYPE_SIZES \
{ \
assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint16 ) == 2 ); \
assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt16 ) == 2 ); \
assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaUint32 ) == 4 ); \
assert( "PortAudio: type sizes are not correct in pa_types.h" && sizeof( PaInt32 ) == 4 ); \
}
#endif /* PA_TYPES_H */

167
pa_common/pa_util.h Normal file
View file

@ -0,0 +1,167 @@
#ifndef PA_UTIL_H
#define PA_UTIL_H
/*
* $Id$
* Portable Audio I/O Library implementation utilities header
* common implementation utilities and interfaces
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, 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.
*/
/** @file
@brief Prototypes for utility functions used by PortAudio implementations.
@todo Document and adhere to the alignment guarantees provided by
PaUtil_AllocateMemory().
*/
#include "portaudio.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
struct PaUtilHostApiRepresentation;
/** Retrieve a specific host API representation. This function can be used
by implementations to retrieve a pointer to their representation in
host api specific extension functions which aren't passed a rep pointer
by pa_front.c.
@param hostApi A pointer to a host API represenation pointer. Apon success
this will receive the requested representation pointer.
@param type A valid host API type identifier.
@returns An error code. If the result is PaNoError then a pointer to the
requested host API representation will be stored in *hostApi. If the host API
specified by type is not found, this function returns paHostApiNotFound.
*/
PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi,
PaHostApiTypeId type );
/** Convert a PortAudio device index into a host API specific device index.
@param hostApiDevice Pointer to a device index, on success this will recieve the
converted device index value.
@param device The PortAudio device index to convert.
@param hostApi The host api which the index should be converted for.
@returns On success returns PaNoError and places the converted index in the
hostApiDevice parameter.
*/
PaError PaUtil_DeviceIndexToHostApiDeviceIndex(
PaDeviceIndex *hostApiDevice, PaDeviceIndex device,
struct PaUtilHostApiRepresentation *hostApi );
/** Set the host error information returned by Pa_GetLastHostErrorInfo. This
function and the paUnanticipatedHostError error code should be used as a
last resort. Implementors should use existing PA error codes where possible,
or nominate new ones. Note that at it is always better to use
PaUtil_SetLastHostErrorInfo() and paUnanticipatedHostError than to return an
ambiguous or inaccurate PaError code.
@param hostApiType The host API which encountered the error (ie of the caller)
@param errorCode The error code returned by the native API function.
@param errorText A string describing the error. PaUtil_SetLastHostErrorInfo
makes a copy of the string, so it is not necessary for the pointer to remain
valid after the call to PaUtil_SetLastHostErrorInfo() returns.
*/
void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
const char *errorText );
/** PA_DEBUG() provides a simple debug message printing facility. The macro
passes it's argument to a printf-like function called PaUtil_DebugPrint()
which prints to stderr and always flushes the stream after printing.
Because preprocessor macros cannot directly accept variable length argument
lists, calls to the macro must include an additional set of parenthesis, eg:
PA_DEBUG(("errorno: %d", 1001 ));
*/
void PaUtil_DebugPrint( const char *format, ... );
#ifdef PA_ENABLE_DEBUG_OUTPUT
#define PA_DEBUG(x) PaUtil_DebugPrint x ;
#else
#define PA_DEBUG(x)
#endif
/* the following functions are implemented in a platform platform specific
.c file
*/
/** Allocate size bytes, guaranteed to be aligned to a FIXME byte boundary */
void *PaUtil_AllocateMemory( long size );
/** Realease block if non-NULL. block may be NULL */
void PaUtil_FreeMemory( void *block );
/** Return the number of currently allocated blocks. This function can be
used for detecting memory leaks.
@note Allocations will only be tracked if PA_TRACK_MEMORY is #defined. If
it isn't, this function will always return 0.
*/
int PaUtil_CountCurrentlyAllocatedBlocks( void );
/** Initialize the clock used by PaUtil_GetTime(). Call this before calling
PaUtil_GetTime.
@see PaUtil_GetTime
*/
void PaUtil_InitializeClock( void );
/** Return the system time in seconds. Used to implement CPU load functions
@see PaUtil_InitializeClock
*/
double PaUtil_GetTime( void );
/* void Pa_Sleep( long msec ); must also be implemented in per-platform .c file */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* PA_UTIL_H */

File diff suppressed because it is too large Load diff

1714
pa_jack/pa_jack.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
#ifndef PA_LINUX_ALSA_H
#define PA_LINUX_ALSA_H
/*
* $Id$
* PortAudio Portable Real-Time Audio Library
* ALSA-specific extensions
*
* 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.
*
*/
/** @file
* ALSA-specific PortAudio API extension header file.
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef struct PaAlsaStreamInfo
{
unsigned long size;
PaHostApiTypeId hostApiType;
unsigned long version;
const char *deviceString;
}
PaAlsaStreamInfo;
void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info );
void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
void PaAlsa_EnableWatchdog( PaStream *s, int enable );
#ifdef __cplusplus
}
#endif
#endif

79
pa_mac/pa_mac_hostapis.c Normal file
View file

@ -0,0 +1,79 @@
/*
* $Id$
* Portable Audio I/O Library Windows initialization table
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, 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.
*/
/** @file
Mac OS host API initialization function table.
*/
#include "pa_hostapi.h"
#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */
PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaError PaMacSm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
PaError PaMacAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
#ifdef __cplusplus
}
#endif /* __cplusplus */
PaUtilHostApiInitializer *paHostApiInitializers[] =
{
#ifdef PA_USE_COREAUDIO
PaMacCore_Initialize,
#endif
#ifdef PA_USE_SM
PaMacSm_Initialize,
#endif
#ifdef PA_USE_JACK
PaJack_Initialize,
#endif
#ifdef PA_USE_ASIO
PaMacAsio_Initialize,
#endif
PaSkeleton_Initialize, /* just for testing */
0 /* NULL terminated array */
};
int paDefaultHostApiIndex = 0;

View file

@ -1,8 +1,23 @@
Notes on Core Audio Implementation of PortAudio
Notes on status of CoreAudio Implementation of PortAudio
Document Last Updated December 9, 2005
There are currently two implementations of PortAudio for Mac Core Audio.
The original is in pa_mac_core_old.c, and the newer, default implementation
is in pa_mac_core.c.
Only pa_mac_core.c is currently developed and supported as it uses apple's
current core audio technology. To select use the old implementation, replace
pa_mac_core.c with pa_mac_core_old.c (eg. "cp pa_mac_core_auhal.c
pa_mac_core.c"), then run configure and make as usual.
----------------------------------------
Notes on Original implementation:
by Phil Burk and Darren Gibbs
Document last updated March 20, 2002
Last updated March 20, 2002
WHAT WORKS
@ -18,7 +33,7 @@ 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.
Built-In audio is typically one CoreAudio device.
Mono doesn't work.
@ -32,3 +47,99 @@ 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.
-------------------------------------------
Notes on Newer/Default AUHAL implementation:
by Bjorn Roche
Last Updated December 9, 2005
Principle of Operation:
This implementation uses AUHAL for audio I/O. To some extent, it also
operates at the "HAL" Layer, though this behavior can be limited by
platform specific flags (see pa_mac_core.h for details). The default
settings should be reasonable: they don't change the SR of the device and
don't cause interruptions if other devices are using the device.
Major Software Elements Used: Apple's HAL AUs provide output SR
conversion transparently, however, only on output, so this
implementation uses AudioConverters to convert the sample rate on input.
A PortAudio ring buffer is used to buffer input when sample rate
conversion is required or when separate audio units are used for duplex
IO. Finally, a PortAudio buffer processor is used to convert formats and
provide additional buffers if needed. Internally, interleaved floating
point data streams are used exclusively - the audio unit converts from
the audio hardware's native format to interleaved float PCM and
PortAudio's Buffer processor is used for conversion to user formats.
Simplex Input: Simplex input uses a single callback. If sample rate
conversion is required, a ring buffer and AudioConverter are used as
well.
Simplex output: Simplex output uses a single callback. No ring buffer or
audio converter is used because AUHAL does its own output SR conversion.
Duplex, one device (no SR conversion): When one device is used, a single
callback is used. This achieves very low latency.
Duplex, separate devices or SR conversion: When SR conversion is
required, data must be buffered before it is converted and data is not
always available at the same times on input and output, so SR conversion
requires the same treatment as separate devices. The input callback
reads data and puts it in the ring buffer. The output callback reads the
data off the ring buffer, into an audio converter and finally to the
buffer processor.
Platform Specific Options:
By using the flags in pa_mac_core.h, the user may specify several options.
For example, the user can specify the sample-rate conversion quality, and
the extent to which PA will attempt to "play nice" and to what extent it
will interrupt other apps to improve performance. For example, if 44100 Hz
sample rate is requested but the device is set at 48000 Hz, PA can either
change the device for optimal playback ("Pro" mode), which may interrupt
other programs playing back audio, or simple use a sample-rate coversion,
which allows for friendlier sharing of the device ("Play Nice" mode).
Known issues:
- Latency: Latency settings are ignored in most cases. Exceptions are when
doing I/O between different devices and as a hint for selecting a realtively
low or relatively high latency in conjunction with
paHostFramesPerBufferUnspecified. Latency settings are always automatically
bound to "safe" values, however, so setting extreme values here should not be
an issue.
- Buffer Size: paHostFramesPerBufferUnspecified and specific host buffer sizes
are supported. paHostFramesPerBufferUnspecified works best in "pro" mode,
where the buffer size and sample rate of the audio device is most likely
to match the expected values.
- Timing info. It reports on stream time, but I'm probably doing something
wrong since patest_sine_time often reports negative latency numbers.
- xrun detection: The only xrun detection performed is when reading
and writing the ring buffer. There is probably more that can be done.
- abort/stop issues: stopping a stream is always a complete operation,
but latency should be low enough to make the lack of a separate abort
unnecessary. Apple clarifies its AudioOutputUnitStop() call here:
http://lists.apple.com/archives/coreaudio-api/2005/Dec/msg00055.html
- blocking interface: Not implemented.
- multichannel: It has been tested successfully on multichannel hardware
from MOTU: traveler and 896HD.
- sample rate conversion quality: By default, SR conversion is the maximum
available. This can be tweaked using flags pa_mac_core.h. Note that the AU
render quyality property is used to set the sample rat conversion quality
as "documented" here:
http://lists.apple.com/archives/coreaudio-api/2004/Jan/msg00141.html
- x86: I haven't tested it on an x86 Mac myself, but users have reported
being able to comiple and run it.

File diff suppressed because it is too large Load diff

69
pa_mac_core/pa_mac_core.h Normal file
View file

@ -0,0 +1,69 @@
/*
* Mac spcific flags for PA.
* portaudio.h should be included before this file.
*/
/*
* A pointer to a paMacCoreStreamInfo may be passed as
* the hostApiSpecificStreamInfo in the PaStreamParameters struct
* when opening a stream. Use NULL, for the defaults. Note that for
* duplex streams, both infos should be the same or behaviour
* is undefined.
*/
typedef struct paMacCoreStreamInfo
{
unsigned long size; /**size of whole structure including this header */
PaHostApiTypeId hostApiType;/**host API for which this data is intended */
unsigned long version; /**structure version */
unsigned long flags; /* flags to modify behaviour */
} paMacCoreStreamInfo;
/* Use this function to initialize a paMacCoreStreamInfo struct
using the requested flags. */
void paSetupMacCoreStreamInfo( paMacCoreStreamInfo *data, unsigned long flags )
{
bzero( data, sizeof( paMacCoreStreamInfo ) );
data->size = sizeof( paMacCoreStreamInfo );
data->hostApiType = paCoreAudio;
data->version = 0x01;
data->flags = flags;
}
/*
* The following flags alter the behaviour of PA on the mac platform.
* they can be ORed together. These should work both for opening and
* checking a device.
*/
/* Allows PortAudio to change things like the device's frame size,
* which allows for much lower latency, but might disrupt the device
* if other programs are using it. */
const unsigned long paMacCore_ChangeDeviceParameters = 0x01;
/* In combination with the above flag,
* causes the stream opening to fail, unless the exact sample rates
* are supported by the device. */
const unsigned long paMacCore_FailIfConversionRequired = 0x02;
/* These flags set the SR conversion quality, if required. The wierd ordering
* allows Maximum Quality to be the default.*/
const unsigned long paMacCore_ConversionQualityMin = 0x0100;
const unsigned long paMacCore_ConversionQualityMedium = 0x0200;
const unsigned long paMacCore_ConversionQualityLow = 0x0300;
const unsigned long paMacCore_ConversionQualityHigh = 0x0400;
const unsigned long paMacCore_ConversionQualityMax = 0x0000;
/*
* Here are some "preset" combinations of flags (above) to get to some
* common configurations. THIS IS OVERKILL, but if more flags are added
* it won't be.
*/
/*This is the default setting: do as much sample rate conversion as possible
* and as little mucking with the device as possible. */
const unsigned long paMacCorePlayNice = 0x00;
/*This setting is tuned for pro audio apps. It allows SR conversion on input
and output, but it tries to set the appropriate SR on the device.*/
const unsigned long paMacCorePro = 0x01;
/*This is a setting to minimize CPU usage and still play nice.*/
const unsigned long paMacCoreMinimizeCPUButPlayNice = 0x0100;
/*This is a setting to minimize CPU usage, even if that means interrupting the device. */
const unsigned long paMacCoreMinimizeCPU = 0x0101;

View file

@ -0,0 +1,484 @@
/* This file is included in pa_mac_core.c. It contains the implementation
* required for blocking I/O. It is separated from pa_mac_core.c simply to ease
* development. */
/*
* Functions for initializing, resetting, and destroying BLIO structures.
*
*/
/* This should be called with the relevant info when initializing a stream for
callback. */
static PaError initializeBlioRingBuffers(
PaMacBlio *blio,
PaSampleFormat inputSampleFormat,
PaSampleFormat outputSampleFormat,
size_t framesPerBuffer,
long ringBufferSize,
int inChan,
int outChan )
{
void *data;
int result;
/* zeroify things */
bzero( blio, sizeof( PaMacBlio ) );
/* this is redundant, but the buffers are used to check
if the bufffers have been initialized, so we do it explicitly. */
blio->inputRingBuffer.buffer = NULL;
blio->outputRingBuffer.buffer = NULL;
/* initialize simple data */
blio->inputSampleFormat = inputSampleFormat;
blio->inputSampleSize = computeSampleSizeFromFormat(inputSampleFormat);
blio->outputSampleFormat = outputSampleFormat;
blio->outputSampleSize = computeSampleSizeFromFormat(outputSampleFormat);
blio->framesPerBuffer = framesPerBuffer;
blio->inChan = inChan;
blio->outChan = outChan;
blio->statusFlags = 0;
blio->errors = paNoError;
#ifdef PA_MAC_BLIO_MUTEX
blio->isInputEmpty = false;
blio->isOutputFull = false;
#endif
/* setup ring buffers */
#ifdef PA_MAC_BLIO_MUTEX
result = PaMacCore_SetUnixError( pthread_mutex_init(&(blio->inputMutex),NULL), 0 );
if( result )
goto error;
result = UNIX_ERR( pthread_cond_init( &(blio->inputCond), NULL ) );
if( result )
goto error;
result = UNIX_ERR( pthread_mutex_init(&(blio->outputMutex),NULL) );
if( result )
goto error;
result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
#endif
if( inChan ) {
data = calloc( ringBufferSize, blio->inputSampleSize );
if( !data )
{
result = paInsufficientMemory;
goto error;
}
assert( 0 == RingBuffer_Init(
&blio->inputRingBuffer,
ringBufferSize*blio->inputSampleSize,
data ) );
}
if( outChan ) {
data = calloc( ringBufferSize, blio->outputSampleSize );
if( !data )
{
result = paInsufficientMemory;
goto error;
}
assert( 0 == RingBuffer_Init(
&blio->outputRingBuffer,
ringBufferSize*blio->outputSampleSize,
data ) );
}
result = resetBlioRingBuffers( blio );
if( result )
goto error;
return 0;
error:
destroyBlioRingBuffers( blio );
return result;
}
#ifdef PA_MAC_BLIO_MUTEX
static PaError blioSetIsInputEmpty( PaMacBlio *blio, bool isEmpty )
{
PaError result = paNoError;
if( isEmpty == blio->isInputEmpty )
goto done;
/* we need to update the value. Here's what we do:
* - Lock the mutex, so noone else can write.
* - update the value.
* - unlock.
* - broadcast to all listeners.
*/
result = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
if( result )
goto done;
blio->isInputEmpty = isEmpty;
result = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
if( result )
goto done;
result = UNIX_ERR( pthread_cond_broadcast( &blio->inputCond ) );
if( result )
goto done;
done:
return result;
}
static PaError blioSetIsOutputFull( PaMacBlio *blio, bool isFull )
{
PaError result = paNoError;
if( isFull == blio->isOutputFull )
goto done;
/* we need to update the value. Here's what we do:
* - Lock the mutex, so noone else can write.
* - update the value.
* - unlock.
* - broadcast to all listeners.
*/
result = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
if( result )
goto done;
blio->isOutputFull = isFull;
result = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
if( result )
goto done;
result = UNIX_ERR( pthread_cond_broadcast( &blio->outputCond ) );
if( result )
goto done;
done:
return result;
}
#endif
/* This should be called after stopping or aborting the stream, so that on next
start, the buffers will be ready. */
static PaError resetBlioRingBuffers( PaMacBlio *blio )
{
int result;
if( blio->outputRingBuffer.buffer ) {
//const int toAdvance = blio->framesPerBuffer*blio->outChan*blio->outputSampleSize;
RingBuffer_Flush( &blio->outputRingBuffer );
bzero( blio->outputRingBuffer.buffer,
blio->outputRingBuffer.bufferSize );
/* Advance buffer */
//if( toAdvance > blio->outputRingBuffer.bufferSize ) {
// result = paInternalError;
// PaUtil_SetLastHostErrorInfo( paCoreAudio, paInternalError, "Output buffer cannot advance more than buffer size." );
// goto error;
//}
RingBuffer_AdvanceWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize ); //toAdvance );
/* Update isOutputFull. */
#ifdef PA_MAC__BLIO_MUTEX
result = blioSetIsOutputFull( blio, toAdvance == blio->outputRingBuffer.bufferSize );
if( result )
goto error;
#endif
/*
printf( "------%d\n" , blio->framesPerBuffer );
printf( "------%d\n" , blio->outChan );
printf( "------%d\n" , blio->outputSampleSize );
printf( "------%d\n" , blio->framesPerBuffer*blio->outChan*blio->outputSampleSize );
*/
}
if( blio->inputRingBuffer.buffer ) {
RingBuffer_Flush( &blio->inputRingBuffer );
bzero( blio->inputRingBuffer.buffer,
blio->inputRingBuffer.bufferSize );
/* Update isInputEmpty. */
#ifdef PA_MAC__BLIO_MUTEX
result = blioSetIsInputEmpty( blio, true );
if( result )
goto error;
#endif
}
return paNoError;
error:
return result;
}
/*This should be called when you are done with the blio. It can safely be called
multiple times if there are no exceptions. */
static PaError destroyBlioRingBuffers( PaMacBlio *blio )
{
PaError result = paNoError;
if( blio->inputRingBuffer.buffer ) {
free( blio->inputRingBuffer.buffer );
#ifdef PA_MAC__BLIO_MUTEX
result = UNIX_ERR( pthread_mutex_destroy( & blio->inputMutex ) );
if( result ) return result;
result = UNIX_ERR( pthread_cond_destroy( & blio->inputCond ) );
if( result ) return result;
#endif
}
blio->inputRingBuffer.buffer = NULL;
if( blio->outputRingBuffer.buffer ) {
free( blio->outputRingBuffer.buffer );
#ifdef PA_MAC__BLIO_MUTEX
result = UNIX_ERR( pthread_mutex_destroy( & blio->outputMutex ) );
if( result ) return result;
result = UNIX_ERR( pthread_cond_destroy( & blio->outputCond ) );
if( result ) return result;
#endif
}
blio->outputRingBuffer.buffer = NULL;
return result;
}
/*
* this is the BlioCallback function. It expects to recieve a PaMacBlio Object
* pointer as userData.
*
*/
static int BlioCallback( const void *input, void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
PaMacBlio *blio = (PaMacBlio*)userData;
long avail;
long toRead;
long toWrite;
/* set flags returned by OS: */
blio->statusFlags |= statusFlags ;
/* --- Handle Input Buffer --- */
if( blio->inChan ) {
avail = RingBuffer_GetWriteAvailable( &blio->inputRingBuffer );
/* check for underflow */
if( avail < frameCount * blio->inputSampleSize * blio->inChan )
blio->statusFlags |= paInputOverflow;
toRead = MIN( avail, frameCount * blio->inputSampleSize * blio->inChan );
/* copy the data */
/*printf( "reading %d\n", toRead );*/
assert( toRead == RingBuffer_Write( &blio->inputRingBuffer, input, toRead ) );
#ifdef PA_MAC__BLIO_MUTEX
/* Priority inversion. See notes below. */
blioSetIsInputEmpty( blio, false );
#endif
}
/* --- Handle Output Buffer --- */
if( blio->outChan ) {
avail = RingBuffer_GetReadAvailable( &blio->outputRingBuffer );
/* check for underflow */
if( avail < frameCount * blio->outputSampleSize * blio->outChan )
blio->statusFlags |= paOutputUnderflow;
toWrite = MIN( avail, frameCount * blio->outputSampleSize * blio->outChan );
if( toWrite != frameCount * blio->outputSampleSize * blio->outChan )
bzero( ((char *)output)+toWrite,
frameCount * blio->outputSampleSize * blio->outChan - toWrite );
/* copy the data */
/*printf( "writing %d\n", toWrite );*/
assert( toWrite == RingBuffer_Read( &blio->outputRingBuffer, output, toWrite ) );
#ifdef PA_MAC__BLIO_MUTEX
/* We have a priority inversion here. However, we will only have to
wait if this was true and is now false, which means we've got
some room in the buffer.
Hopefully problems will be minimized. */
blioSetIsOutputFull( blio, false );
#endif
}
return paContinue;
}
/* FIXME: test that buffer is fully played out on stop */
static PaError ReadStream( PaStream* stream,
void *buffer,
unsigned long frames )
{
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
char *cbuf = (char *) buffer;
PaError ret = paNoError;
VVDBUG(("ReadStream()\n"));
while( frames > 0 ) {
long avail;
long toRead;
do {
avail = RingBuffer_GetReadAvailable( &blio->inputRingBuffer );
/*
printf( "Read Buffer is %%%g full: %ld of %ld.\n",
100 * (float)avail / (float) blio->inputRingBuffer.bufferSize,
avail, blio->inputRingBuffer.bufferSize );
*/
if( avail == 0 ) {
#ifdef PA_MAC_BLIO_MUTEX
/**block when empty*/
ret = UNIX_ERR( pthread_mutex_lock( &blio->inputMutex ) );
if( ret )
return ret;
while( blio->isInputEmpty ) {
ret = UNIX_ERR( pthread_cond_wait( &blio->inputCond, &blio->inputMutex ) );
if( ret )
return ret;
}
ret = UNIX_ERR( pthread_mutex_unlock( &blio->inputMutex ) );
if( ret )
return ret;
#else
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
#endif
}
} while( avail == 0 );
toRead = MIN( avail, frames * blio->inputSampleSize * blio->inChan );
toRead -= toRead % blio->inputSampleSize * blio->inChan ;
RingBuffer_Read( &blio->inputRingBuffer, (void *)cbuf, toRead );
cbuf += toRead;
frames -= toRead / ( blio->inputSampleSize * blio->inChan );
if( toRead == avail ) {
#ifdef PA_MAC_BLIO_MUTEX
/* we just emptied the buffer, so we need to mark it as empty. */
ret = blioSetIsInputEmpty( blio, true );
if( ret )
return ret;
/* of course, in the meantime, the callback may have put some sats
in, so
so check for that, too, to avoid a race condition. */
if( RingBuffer_GetReadAvailable( &blio->inputRingBuffer ) ) {
blioSetIsInputEmpty( blio, false );
if( ret )
return ret;
}
#endif
}
}
/* Report either paNoError or paInputOverflowed. */
/* may also want to report other errors, but this is non-standard. */
ret = blio->statusFlags & paInputOverflow;
/* report overflow only once: */
blio->statusFlags &= ~paInputOverflow;
if (ret)
ret = paInputOverflowed;
return ret;
}
static PaError WriteStream( PaStream* stream,
const void *buffer,
unsigned long frames )
{
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
char *cbuf = (char *) buffer;
PaError ret = paNoError;
VVDBUG(("WriteStream()\n"));
while( frames > 0 ) {
long avail = 0;
long toWrite;
do {
avail = RingBuffer_GetWriteAvailable( &blio->outputRingBuffer );
/*
printf( "Write Buffer is %%%g full: %ld of %ld.\n",
100 - 100 * (float)avail / (float) blio->outputRingBuffer.bufferSize,
avail, blio->outputRingBuffer.bufferSize );
*/
if( avail == 0 ) {
#ifdef PA_MAC_BLIO_MUTEX
/*block while full*/
ret = UNIX_ERR( pthread_mutex_lock( &blio->outputMutex ) );
if( ret )
return ret;
while( blio->isOutputFull ) {
ret = UNIX_ERR( pthread_cond_wait( &blio->outputCond, &blio->outputMutex ) );
if( ret )
return ret;
}
ret = UNIX_ERR( pthread_mutex_unlock( &blio->outputMutex ) );
if( ret )
return ret;
#else
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
#endif
}
} while( avail == 0 );
toWrite = MIN( avail, frames * blio->outputSampleSize * blio->outChan );
toWrite -= toWrite % blio->outputSampleSize * blio->outChan ;
RingBuffer_Write( &blio->outputRingBuffer, (void *)cbuf, toWrite );
cbuf += toWrite;
frames -= toWrite / ( blio->outputSampleSize * blio->outChan );
#ifdef PA_MAC_BLIO_MUTEX
if( toWrite == avail ) {
/* we just filled up the buffer, so we need to mark it as filled. */
ret = blioSetIsOutputFull( blio, true );
if( ret )
return ret;
/* of course, in the meantime, we may have emptied the buffer, so
so check for that, too, to avoid a race condition. */
if( RingBuffer_GetWriteAvailable( &blio->outputRingBuffer ) ) {
blioSetIsOutputFull( blio, false );
if( ret )
return ret;
}
}
#endif
}
/* Report either paNoError or paOutputUnderflowed. */
/* may also want to report other errors, but this is non-standard. */
ret = blio->statusFlags & paOutputUnderflow;
/* FIXME: restting this flag is not MP/SMP safe. use compare_and_Swap wherever these flags are changed. */
/* report underflow only once: */
blio->statusFlags &= ~paOutputUnderflow;
if (ret)
ret = paOutputUnderflowed;
return ret;
}
/*
*
*/
static void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio )
{
if( blio->outputRingBuffer.buffer ) {
long avail = RingBuffer_GetWriteAvailable( &blio->outputRingBuffer );
while( avail != blio->outputRingBuffer.bufferSize ) {
/*FIXME: do a true block.*/
if( avail == 0 )
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
avail = RingBuffer_GetWriteAvailable( &blio->outputRingBuffer );
}
}
}
static signed long GetStreamReadAvailable( PaStream* stream )
{
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
VVDBUG(("GetStreamReadAvailable()\n"));
return RingBuffer_GetReadAvailable( &blio->inputRingBuffer )
/ ( blio->outputSampleSize * blio->outChan );
}
static signed long GetStreamWriteAvailable( PaStream* stream )
{
PaMacBlio *blio = & ((PaMacCoreStream*)stream) -> blio;
VVDBUG(("GetStreamWriteAvailable()\n"));
return RingBuffer_GetWriteAvailable( &blio->outputRingBuffer )
/ ( blio->outputSampleSize * blio->outChan );
}

View file

@ -0,0 +1,82 @@
/*
* Number of miliseconds to busy wait whil waiting for data in blocking calls.
*/
#define PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL (5)
/*
*Define exactly one of these blocking methods
*/
#define PA_MAC_BLIO_BUSY_WAIT
/*
#define PA_MAC_BLIO_MUTEX
*/
typedef struct {
RingBuffer inputRingBuffer;
RingBuffer outputRingBuffer;
PaSampleFormat inputSampleFormat;
size_t inputSampleSize;
PaSampleFormat outputSampleFormat;
size_t outputSampleSize;
size_t framesPerBuffer;
int inChan;
int outChan;
PaStreamCallbackFlags statusFlags;
PaError errors;
/* Here we handle blocking, using condition variables. */
#ifdef PA_MAC_BLIO_MUTEX
volatile bool isInputEmpty;
pthread_mutex_t inputMutex;
pthread_cond_t inputCond;
volatile bool isOutputFull;
pthread_mutex_t outputMutex;
pthread_cond_t outputCond;
#endif
}
PaMacBlio;
/*
* These functions operate on condition and related variables.
*/
/*
* This fnuction determines the size of a particular sample format.
* if the format is not recognized, this returns zero.
*/
static size_t computeSampleSizeFromFormat( PaSampleFormat format )
{
switch( format ) {
case paFloat32: return 4;
case paInt32: return 4;
case paInt24: return 3;
case paInt16: return 2;
case paInt8: case paUInt8: return 1;
default: return 0;
}
}
static PaError initializeBlioRingBuffers(
PaMacBlio *blio,
PaSampleFormat inputSampleFormat,
PaSampleFormat outputSampleFormat,
size_t framesPerBuffer,
long ringBufferSize,
int inChan,
int outChan );
static PaError destroyBlioRingBuffers( PaMacBlio *blio );
static PaError resetBlioRingBuffers( PaMacBlio *blio );
static int BlioCallback(
const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData );
static void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio );

View file

@ -0,0 +1,907 @@
/*
* $Id$
* pa_mac_core.c
* Implementation of PortAudio for Mac OS X CoreAudio
*
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
*
* Authors: Ross Bencina and Phil Burk
* 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 <CoreAudio/CoreAudio.h>
#include <AudioToolbox/AudioToolbox.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "portaudio.h"
#include "pa_trace.h"
#include "pa_util.h"
#include "pa_allocation.h"
#include "pa_hostapi.h"
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
// ===== constants =====
// ===== structs =====
#pragma mark structs
// PaMacCoreHostApiRepresentation - host api datastructure specific to this implementation
typedef struct PaMacCore_HAR
{
PaUtilHostApiRepresentation inheritedHostApiRep;
PaUtilStreamInterface callbackStreamInterface;
PaUtilStreamInterface blockingStreamInterface;
PaUtilAllocationGroup *allocations;
AudioDeviceID *macCoreDeviceIds;
}
PaMacCoreHostApiRepresentation;
typedef struct PaMacCore_DI
{
PaDeviceInfo inheritedDeviceInfo;
}
PaMacCoreDeviceInfo;
// PaMacCoreStream - a stream data structure specifically for this implementation
typedef struct PaMacCore_S
{
PaUtilStreamRepresentation streamRepresentation;
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
PaUtilBufferProcessor bufferProcessor;
int primeStreamUsingCallback;
AudioDeviceID inputDevice;
AudioDeviceID outputDevice;
// Processing thread management --------------
// HANDLE abortEvent;
// HANDLE processingThread;
// DWORD processingThreadId;
char throttleProcessingThreadOnOverload; // 0 -> don't throtte, non-0 -> throttle
int processingThreadPriority;
int highThreadPriority;
int throttledThreadPriority;
unsigned long throttledSleepMsecs;
int isStopped;
volatile int isActive;
volatile int stopProcessing; // stop thread once existing buffers have been returned
volatile int abortProcessing; // stop thread immediately
// DWORD allBuffersDurationMs; // used to calculate timeouts
}
PaMacCoreStream;
// Data needed by the CoreAudio callback functions
typedef struct PaMacCore_CD
{
PaMacCoreStream *stream;
PaStreamCallback *callback;
void *userData;
PaUtilConverter *inputConverter;
PaUtilConverter *outputConverter;
void *inputBuffer;
void *outputBuffer;
int inputChannelCount;
int outputChannelCount;
PaSampleFormat inputSampleFormat;
PaSampleFormat outputSampleFormat;
PaUtilTriangularDitherGenerator *ditherGenerator;
}
PaMacClientData;
// ===== CoreAudio-PortAudio bridge functions =====
#pragma mark CoreAudio-PortAudio bridge functions
// Maps CoreAudio OSStatus codes to PortAudio PaError codes
static PaError conv_err(OSStatus error)
{
PaError result;
switch (error) {
case kAudioHardwareNoError:
result = paNoError; break;
case kAudioHardwareNotRunningError:
result = paInternalError; break;
case kAudioHardwareUnspecifiedError:
result = paInternalError; break;
case kAudioHardwareUnknownPropertyError:
result = paInternalError; break;
case kAudioHardwareBadPropertySizeError:
result = paInternalError; break;
case kAudioHardwareIllegalOperationError:
result = paInternalError; break;
case kAudioHardwareBadDeviceError:
result = paInvalidDevice; break;
case kAudioHardwareBadStreamError:
result = paBadStreamPtr; break;
case kAudioHardwareUnsupportedOperationError:
result = paInternalError; break;
case kAudioDeviceUnsupportedFormatError:
result = paSampleFormatNotSupported; break;
case kAudioDevicePermissionsError:
result = paDeviceUnavailable; break;
default:
result = paInternalError;
}
return result;
}
/* This function is unused
static AudioStreamBasicDescription *InitializeStreamDescription(const PaStreamParameters *parameters, double sampleRate)
{
struct AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(sizeof(AudioStreamBasicDescription));
streamDescription->mSampleRate = sampleRate;
streamDescription->mFormatID = kAudioFormatLinearPCM;
streamDescription->mFormatFlags = 0;
streamDescription->mFramesPerPacket = 1;
if (parameters->sampleFormat & paNonInterleaved) {
streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsNonInterleaved;
streamDescription->mChannelsPerFrame = 1;
streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat);
streamDescription->mBytesPerPacket = Pa_GetSampleSize(parameters->sampleFormat);
}
else {
streamDescription->mChannelsPerFrame = parameters->channelCount;
}
streamDescription->mBytesPerFrame = Pa_GetSampleSize(parameters->sampleFormat) * streamDescription->mChannelsPerFrame;
streamDescription->mBytesPerPacket = streamDescription->mBytesPerFrame * streamDescription->mFramesPerPacket;
if (parameters->sampleFormat & paFloat32) {
streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsFloat;
streamDescription->mBitsPerChannel = 32;
}
else if (parameters->sampleFormat & paInt32) {
streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
streamDescription->mBitsPerChannel = 32;
}
else if (parameters->sampleFormat & paInt24) {
streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
streamDescription->mBitsPerChannel = 24;
}
else if (parameters->sampleFormat & paInt16) {
streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
streamDescription->mBitsPerChannel = 16;
}
else if (parameters->sampleFormat & paInt8) {
streamDescription->mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
streamDescription->mBitsPerChannel = 8;
}
else if (parameters->sampleFormat & paInt32) {
streamDescription->mBitsPerChannel = 8;
}
return streamDescription;
}
*/
static PaStreamCallbackTimeInfo *InitializeTimeInfo(const AudioTimeStamp* now, const AudioTimeStamp* inputTime, const AudioTimeStamp* outputTime)
{
PaStreamCallbackTimeInfo *timeInfo = PaUtil_AllocateMemory(sizeof(PaStreamCallbackTimeInfo));
timeInfo->inputBufferAdcTime = inputTime->mSampleTime;
timeInfo->currentTime = now->mSampleTime;
timeInfo->outputBufferDacTime = outputTime->mSampleTime;
return timeInfo;
}
// ===== support functions =====
#pragma mark support functions
static void CleanUp(PaMacCoreHostApiRepresentation *macCoreHostApi)
{
if( macCoreHostApi->allocations )
{
PaUtil_FreeAllAllocations( macCoreHostApi->allocations );
PaUtil_DestroyAllocationGroup( macCoreHostApi->allocations );
}
PaUtil_FreeMemory( macCoreHostApi );
}
static PaError GetChannelInfo(PaDeviceInfo *deviceInfo, AudioDeviceID macCoreDeviceId, int isInput)
{
UInt32 propSize;
PaError err = paNoError;
UInt32 i;
int numChannels = 0;
AudioBufferList *buflist;
err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
buflist = PaUtil_AllocateMemory(propSize);
err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamConfiguration, &propSize, buflist));
if (!err) {
for (i = 0; i < buflist->mNumberBuffers; ++i) {
numChannels += buflist->mBuffers[i].mNumberChannels;
}
if (isInput)
deviceInfo->maxInputChannels = numChannels;
else
deviceInfo->maxOutputChannels = numChannels;
int frameLatency;
propSize = sizeof(UInt32);
err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyLatency, &propSize, &frameLatency));
if (!err) {
double secondLatency = frameLatency / deviceInfo->defaultSampleRate;
if (isInput) {
deviceInfo->defaultLowInputLatency = secondLatency;
deviceInfo->defaultHighInputLatency = secondLatency;
}
else {
deviceInfo->defaultLowOutputLatency = secondLatency;
deviceInfo->defaultHighOutputLatency = secondLatency;
}
}
}
PaUtil_FreeMemory(buflist);
return err;
}
static PaError InitializeDeviceInfo(PaMacCoreDeviceInfo *macCoreDeviceInfo, AudioDeviceID macCoreDeviceId, PaHostApiIndex hostApiIndex )
{
PaDeviceInfo *deviceInfo = &macCoreDeviceInfo->inheritedDeviceInfo;
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
PaError err = paNoError;
UInt32 propSize;
err = conv_err(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
// FIXME: this allocation should be part of the allocations group
char *name = PaUtil_AllocateMemory(propSize);
err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, name));
if (!err) {
deviceInfo->name = name;
}
Float64 sampleRate;
propSize = sizeof(Float64);
err = conv_err(AudioDeviceGetProperty(macCoreDeviceId, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &sampleRate));
if (!err) {
deviceInfo->defaultSampleRate = sampleRate;
}
// Get channel info
err = GetChannelInfo(deviceInfo, macCoreDeviceId, 1);
err = GetChannelInfo(deviceInfo, macCoreDeviceId, 0);
return err;
}
static PaError InitializeDeviceInfos( PaMacCoreHostApiRepresentation *macCoreHostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
PaUtilHostApiRepresentation *hostApi;
PaMacCoreDeviceInfo *deviceInfoArray;
// initialise device counts and default devices under the assumption that there are no devices. These values are incremented below if and when devices are successfully initialized.
hostApi = &macCoreHostApi->inheritedHostApiRep;
hostApi->info.deviceCount = 0;
hostApi->info.defaultInputDevice = paNoDevice;
hostApi->info.defaultOutputDevice = paNoDevice;
UInt32 propsize;
AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL);
int numDevices = propsize / sizeof(AudioDeviceID);
hostApi->info.deviceCount = numDevices;
if (numDevices > 0) {
hostApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
macCoreHostApi->allocations, sizeof(PaDeviceInfo*) * numDevices );
if( !hostApi->deviceInfos )
{
return paInsufficientMemory;
}
// allocate all device info structs in a contiguous block
deviceInfoArray = (PaMacCoreDeviceInfo*)PaUtil_GroupAllocateMemory(
macCoreHostApi->allocations, sizeof(PaMacCoreDeviceInfo) * numDevices );
if( !deviceInfoArray )
{
return paInsufficientMemory;
}
macCoreHostApi->macCoreDeviceIds = PaUtil_GroupAllocateMemory(macCoreHostApi->allocations, propsize);
AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, macCoreHostApi->macCoreDeviceIds);
AudioDeviceID defaultInputDevice, defaultOutputDevice;
propsize = sizeof(AudioDeviceID);
AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &propsize, &defaultInputDevice);
AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propsize, &defaultOutputDevice);
UInt32 i;
for (i = 0; i < numDevices; ++i) {
if (macCoreHostApi->macCoreDeviceIds[i] == defaultInputDevice) {
hostApi->info.defaultInputDevice = i;
}
if (macCoreHostApi->macCoreDeviceIds[i] == defaultOutputDevice) {
hostApi->info.defaultOutputDevice = i;
}
InitializeDeviceInfo(&deviceInfoArray[i], macCoreHostApi->macCoreDeviceIds[i], hostApiIndex);
hostApi->deviceInfos[i] = &(deviceInfoArray[i].inheritedDeviceInfo);
}
}
return result;
}
static OSStatus CheckFormat(AudioDeviceID macCoreDeviceId, const PaStreamParameters *parameters, double sampleRate, int isInput)
{
UInt32 propSize = sizeof(AudioStreamBasicDescription);
AudioStreamBasicDescription *streamDescription = PaUtil_AllocateMemory(propSize);
streamDescription->mSampleRate = sampleRate;
streamDescription->mFormatID = 0;
streamDescription->mFormatFlags = 0;
streamDescription->mBytesPerPacket = 0;
streamDescription->mFramesPerPacket = 0;
streamDescription->mBytesPerFrame = 0;
streamDescription->mChannelsPerFrame = 0;
streamDescription->mBitsPerChannel = 0;
streamDescription->mReserved = 0;
OSStatus result = AudioDeviceGetProperty(macCoreDeviceId, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &propSize, streamDescription);
PaUtil_FreeMemory(streamDescription);
return result;
}
static OSStatus CopyInputData(PaMacClientData* destination, const AudioBufferList *source, unsigned long frameCount)
{
int frameSpacing, channelSpacing;
if (destination->inputSampleFormat & paNonInterleaved) {
frameSpacing = 1;
channelSpacing = destination->inputChannelCount;
}
else {
frameSpacing = destination->inputChannelCount;
channelSpacing = 1;
}
AudioBuffer const *inputBuffer = &source->mBuffers[0];
void *coreAudioBuffer = inputBuffer->mData;
void *portAudioBuffer = destination->inputBuffer;
UInt32 i, streamNumber, streamChannel;
for (i = streamNumber = streamChannel = 0; i < destination->inputChannelCount; ++i, ++streamChannel) {
if (streamChannel >= inputBuffer->mNumberChannels) {
++streamNumber;
inputBuffer = &source->mBuffers[streamNumber];
coreAudioBuffer = inputBuffer->mData;
streamChannel = 0;
}
destination->inputConverter(portAudioBuffer, frameSpacing, coreAudioBuffer, inputBuffer->mNumberChannels, frameCount, destination->ditherGenerator);
coreAudioBuffer += sizeof(Float32);
portAudioBuffer += Pa_GetSampleSize(destination->inputSampleFormat) * channelSpacing;
}
return noErr;
}
static OSStatus CopyOutputData(AudioBufferList* destination, PaMacClientData *source, unsigned long frameCount)
{
int frameSpacing, channelSpacing;
if (source->outputSampleFormat & paNonInterleaved) {
frameSpacing = 1;
channelSpacing = source->outputChannelCount;
}
else {
frameSpacing = source->outputChannelCount;
channelSpacing = 1;
}
AudioBuffer *outputBuffer = &destination->mBuffers[0];
void *coreAudioBuffer = outputBuffer->mData;
void *portAudioBuffer = source->outputBuffer;
UInt32 i, streamNumber, streamChannel;
for (i = streamNumber = streamChannel = 0; i < source->outputChannelCount; ++i, ++streamChannel) {
if (streamChannel >= outputBuffer->mNumberChannels) {
++streamNumber;
outputBuffer = &destination->mBuffers[streamNumber];
coreAudioBuffer = outputBuffer->mData;
streamChannel = 0;
}
source->outputConverter(coreAudioBuffer, outputBuffer->mNumberChannels, portAudioBuffer, frameSpacing, frameCount, NULL);
coreAudioBuffer += sizeof(Float32);
portAudioBuffer += Pa_GetSampleSize(source->outputSampleFormat) * channelSpacing;
}
return noErr;
}
static OSStatus AudioIOProc( AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
const AudioTimeStamp* inInputTime,
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* inClientData)
{
PaMacClientData *clientData = (PaMacClientData *)inClientData;
PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
if (clientData->inputBuffer) {
CopyInputData(clientData, inInputData, frameCount);
}
PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
if (clientData->outputBuffer) {
CopyOutputData(outOutputData, clientData, frameCount);
}
PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
if (result == paComplete || result == paAbort) {
Pa_StopStream(clientData->stream);
}
PaUtil_FreeMemory( timeInfo );
return noErr;
}
// This is not for input-only streams, this is for streams where the input device is different from the output device
static OSStatus AudioInputProc( AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
const AudioTimeStamp* inInputTime,
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* inClientData)
{
PaMacClientData *clientData = (PaMacClientData *)inClientData;
PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
AudioBuffer const *inputBuffer = &inInputData->mBuffers[0];
unsigned long frameCount = inputBuffer->mDataByteSize / (inputBuffer->mNumberChannels * sizeof(Float32));
CopyInputData(clientData, inInputData, frameCount);
PaStreamCallbackResult result = clientData->callback(clientData->inputBuffer, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
if( result == paComplete || result == paAbort )
Pa_StopStream(clientData->stream);
PaUtil_FreeMemory( timeInfo );
return noErr;
}
// This is not for output-only streams, this is for streams where the input device is different from the output device
static OSStatus AudioOutputProc( AudioDeviceID inDevice,
const AudioTimeStamp* inNow,
const AudioBufferList* inInputData,
const AudioTimeStamp* inInputTime,
AudioBufferList* outOutputData,
const AudioTimeStamp* inOutputTime,
void* inClientData)
{
PaMacClientData *clientData = (PaMacClientData *)inClientData;
//PaStreamCallbackTimeInfo *timeInfo = InitializeTimeInfo(inNow, inInputTime, inOutputTime);
PaUtil_BeginCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer );
AudioBuffer *outputBuffer = &outOutputData->mBuffers[0];
unsigned long frameCount = outputBuffer->mDataByteSize / (outputBuffer->mNumberChannels * sizeof(Float32));
//clientData->callback(NULL, clientData->outputBuffer, frameCount, timeInfo, paNoFlag, clientData->userData);
CopyOutputData(outOutputData, clientData, frameCount);
PaUtil_EndCpuLoadMeasurement( &clientData->stream->cpuLoadMeasurer, frameCount );
return noErr;
}
static PaError SetSampleRate(AudioDeviceID device, double sampleRate, int isInput)
{
PaError result = paNoError;
double actualSampleRate;
UInt32 propSize = sizeof(double);
result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyNominalSampleRate, propSize, &sampleRate));
result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSampleRate));
if (result == paNoError && actualSampleRate != sampleRate) {
result = paInvalidSampleRate;
}
return result;
}
static PaError SetFramesPerBuffer(AudioDeviceID device, unsigned long framesPerBuffer, int isInput)
{
PaError result = paNoError;
UInt32 preferredFramesPerBuffer = framesPerBuffer;
// while (preferredFramesPerBuffer > UINT32_MAX) {
// preferredFramesPerBuffer /= 2;
// }
UInt32 actualFramesPerBuffer;
UInt32 propSize = sizeof(UInt32);
result = conv_err(AudioDeviceSetProperty(device, NULL, 0, isInput, kAudioDevicePropertyBufferFrameSize, propSize, &preferredFramesPerBuffer));
result = conv_err(AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyBufferFrameSize, &propSize, &actualFramesPerBuffer));
if (result != paNoError) {
// do nothing
}
else if (actualFramesPerBuffer > framesPerBuffer) {
result = paBufferTooSmall;
}
else if (actualFramesPerBuffer < framesPerBuffer) {
result = paBufferTooBig;
}
return result;
}
static PaError SetUpUnidirectionalStream(AudioDeviceID device, double sampleRate, unsigned long framesPerBuffer, int isInput)
{
PaError err = paNoError;
err = SetSampleRate(device, sampleRate, isInput);
if( err == paNoError )
err = SetFramesPerBuffer(device, framesPerBuffer, isInput);
return err;
}
// ===== PortAudio functions =====
#pragma mark PortAudio functions
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
#ifdef __cplusplus
}
#endif // __cplusplus
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
CleanUp(macCoreHostApi);
}
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate )
{
PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation*)hostApi;
PaDeviceInfo *deviceInfo;
PaError result = paNoError;
if (inputParameters) {
deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
if (inputParameters->channelCount > deviceInfo->maxInputChannels)
result = paInvalidChannelCount;
else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[inputParameters->device], inputParameters, sampleRate, 1) != kAudioHardwareNoError) {
result = paInvalidSampleRate;
}
}
if (outputParameters && result == paNoError) {
deviceInfo = macCoreHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
if (outputParameters->channelCount > deviceInfo->maxOutputChannels)
result = paInvalidChannelCount;
else if (CheckFormat(macCoreHostApi->macCoreDeviceIds[outputParameters->device], outputParameters, sampleRate, 0) != kAudioHardwareNoError) {
result = paInvalidSampleRate;
}
}
return result;
}
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaStream** s,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate,
unsigned long framesPerBuffer,
PaStreamFlags streamFlags,
PaStreamCallback *streamCallback,
void *userData )
{
PaError err = paNoError;
PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)hostApi;
PaMacCoreStream *stream = PaUtil_AllocateMemory(sizeof(PaMacCoreStream));
stream->isActive = 0;
stream->isStopped = 1;
stream->inputDevice = kAudioDeviceUnknown;
stream->outputDevice = kAudioDeviceUnknown;
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
( (streamCallback)
? &macCoreHostApi->callbackStreamInterface
: &macCoreHostApi->blockingStreamInterface ),
streamCallback, userData );
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
*s = (PaStream*)stream;
PaMacClientData *clientData = PaUtil_AllocateMemory(sizeof(PaMacClientData));
clientData->stream = stream;
clientData->callback = streamCallback;
clientData->userData = userData;
clientData->inputBuffer = 0;
clientData->outputBuffer = 0;
clientData->ditherGenerator = PaUtil_AllocateMemory(sizeof(PaUtilTriangularDitherGenerator));
PaUtil_InitializeTriangularDitherState(clientData->ditherGenerator);
if (inputParameters != NULL) {
stream->inputDevice = macCoreHostApi->macCoreDeviceIds[inputParameters->device];
clientData->inputConverter = PaUtil_SelectConverter(paFloat32, inputParameters->sampleFormat, streamFlags);
clientData->inputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(inputParameters->sampleFormat) * framesPerBuffer * inputParameters->channelCount);
clientData->inputChannelCount = inputParameters->channelCount;
clientData->inputSampleFormat = inputParameters->sampleFormat;
err = SetUpUnidirectionalStream(stream->inputDevice, sampleRate, framesPerBuffer, 1);
}
if (err == paNoError && outputParameters != NULL) {
stream->outputDevice = macCoreHostApi->macCoreDeviceIds[outputParameters->device];
clientData->outputConverter = PaUtil_SelectConverter(outputParameters->sampleFormat, paFloat32, streamFlags);
clientData->outputBuffer = PaUtil_AllocateMemory(Pa_GetSampleSize(outputParameters->sampleFormat) * framesPerBuffer * outputParameters->channelCount);
clientData->outputChannelCount = outputParameters->channelCount;
clientData->outputSampleFormat = outputParameters->sampleFormat;
err = SetUpUnidirectionalStream(stream->outputDevice, sampleRate, framesPerBuffer, 0);
}
if (inputParameters == NULL || outputParameters == NULL || stream->inputDevice == stream->outputDevice) {
AudioDeviceID device = (inputParameters == NULL) ? stream->outputDevice : stream->inputDevice;
AudioDeviceAddIOProc(device, AudioIOProc, clientData);
}
else {
// using different devices for input and output
AudioDeviceAddIOProc(stream->inputDevice, AudioInputProc, clientData);
AudioDeviceAddIOProc(stream->outputDevice, AudioOutputProc, clientData);
}
return err;
}
// Note: When CloseStream() is called, the multi-api layer ensures that the stream has already been stopped or aborted.
static PaError CloseStream( PaStream* s )
{
PaError err = paNoError;
PaMacCoreStream *stream = (PaMacCoreStream*)s;
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
if (stream->inputDevice != kAudioDeviceUnknown) {
if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioIOProc));
}
else {
err = conv_err(AudioDeviceRemoveIOProc(stream->inputDevice, AudioInputProc));
err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioOutputProc));
}
}
else {
err = conv_err(AudioDeviceRemoveIOProc(stream->outputDevice, AudioIOProc));
}
return err;
}
static PaError StartStream( PaStream *s )
{
PaError err = paNoError;
PaMacCoreStream *stream = (PaMacCoreStream*)s;
if (stream->inputDevice != kAudioDeviceUnknown) {
if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
err = conv_err(AudioDeviceStart(stream->inputDevice, AudioIOProc));
}
else {
err = conv_err(AudioDeviceStart(stream->inputDevice, AudioInputProc));
err = conv_err(AudioDeviceStart(stream->outputDevice, AudioOutputProc));
}
}
else {
err = conv_err(AudioDeviceStart(stream->outputDevice, AudioIOProc));
}
stream->isActive = 1;
stream->isStopped = 0;
return err;
}
static PaError AbortStream( PaStream *s )
{
PaError err = paNoError;
PaMacCoreStream *stream = (PaMacCoreStream*)s;
if (stream->inputDevice != kAudioDeviceUnknown) {
if (stream->outputDevice == kAudioDeviceUnknown || stream->outputDevice == stream->inputDevice) {
err = conv_err(AudioDeviceStop(stream->inputDevice, AudioIOProc));
}
else {
err = conv_err(AudioDeviceStop(stream->inputDevice, AudioInputProc));
err = conv_err(AudioDeviceStop(stream->outputDevice, AudioOutputProc));
}
}
else {
err = conv_err(AudioDeviceStop(stream->outputDevice, AudioIOProc));
}
stream->isActive = 0;
stream->isStopped = 1;
return err;
}
static PaError StopStream( PaStream *s )
{
// TODO: this should be nicer than abort
return AbortStream(s);
}
static PaError IsStreamStopped( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
return stream->isStopped;
}
static PaError IsStreamActive( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
return stream->isActive;
}
static PaTime GetStreamTime( PaStream *s )
{
OSStatus err;
PaTime result;
PaMacCoreStream *stream = (PaMacCoreStream*)s;
AudioTimeStamp *timeStamp = PaUtil_AllocateMemory(sizeof(AudioTimeStamp));
if (stream->inputDevice != kAudioDeviceUnknown) {
err = AudioDeviceGetCurrentTime(stream->inputDevice, timeStamp);
}
else {
err = AudioDeviceGetCurrentTime(stream->outputDevice, timeStamp);
}
result = err ? 0 : timeStamp->mSampleTime;
PaUtil_FreeMemory(timeStamp);
return result;
}
static double GetStreamCpuLoad( PaStream* s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
}
// As separate stream interfaces are used for blocking and callback streams, the following functions can be guaranteed to only be called for blocking streams.
static PaError ReadStream( PaStream* s,
void *buffer,
unsigned long frames )
{
return paInternalError;
}
static PaError WriteStream( PaStream* s,
const void *buffer,
unsigned long frames )
{
return paInternalError;
}
static signed long GetStreamReadAvailable( PaStream* s )
{
return paInternalError;
}
static signed long GetStreamWriteAvailable( PaStream* s )
{
return paInternalError;
}
// HostAPI-specific initialization function
PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
PaMacCoreHostApiRepresentation *macCoreHostApi = (PaMacCoreHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaMacCoreHostApiRepresentation) );
if( !macCoreHostApi )
{
result = paInsufficientMemory;
goto error;
}
macCoreHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !macCoreHostApi->allocations )
{
result = paInsufficientMemory;
goto error;
}
*hostApi = &macCoreHostApi->inheritedHostApiRep;
(*hostApi)->info.structVersion = 1;
(*hostApi)->info.type = paCoreAudio;
(*hostApi)->info.name = "CoreAudio";
result = InitializeDeviceInfos(macCoreHostApi, hostApiIndex);
if (result != paNoError) {
goto error;
}
// Set up the proper callbacks to this HostApi's functions
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
PaUtil_InitializeStreamInterface( &macCoreHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
GetStreamTime, GetStreamCpuLoad,
PaUtil_DummyRead, PaUtil_DummyWrite,
PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
PaUtil_InitializeStreamInterface( &macCoreHostApi->blockingStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
GetStreamTime, PaUtil_DummyGetCpuLoad,
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
return result;
error:
if( macCoreHostApi ) {
CleanUp(macCoreHostApi);
}
return result;
}

View file

@ -0,0 +1,571 @@
/*
*
* pa_mac_core_utilities.c
*
* utilitiy functions for pa_mac_core.c
*
* This functions are more like helper functions than part of the core,
* so I moved them to a separate file so the core code would be cleaner.
*
* by Bjorn Roche.
*/
#define UNIX_ERR(err) PaMacCore_SetUnixError( err, __LINE__ )
static PaError PaMacCore_SetUnixError( int err, int line )
{
PaError ret;
const char *errorText;
if( err == 0 )
{
return paNoError;
}
ret = paNoError;
errorText = strerror( err );
/** Map Unix error to PaError. Pretty much the only one that maps
is ENOMEM. */
if( err == ENOMEM )
ret = paInsufficientMemory;
else
ret = paInternalError;
DBUG(("%d on line %d: msg='%s'\n", err, line, errorText));
PaUtil_SetLastHostErrorInfo( paCoreAudio, err, errorText );
return ret;
}
/*
* Translates MacOS generated errors into PaErrors
*/
static PaError PaMacCore_SetError(OSStatus error, int line, int isError)
{
/*FIXME: still need to handle possible ComponentResult values.*/
/* unfortunately, they don't seem to be documented anywhere.*/
PaError result;
const char *errorType;
const char *errorText;
switch (error) {
case kAudioHardwareNoError:
return paNoError;
case kAudioHardwareNotRunningError:
errorText = "Audio Hardware Not Running";
result = paInternalError; break;
case kAudioHardwareUnspecifiedError:
errorText = "Unspecified Audio Hardware Error";
result = paInternalError; break;
case kAudioHardwareUnknownPropertyError:
errorText = "Audio Hardware: Unknown Property";
result = paInternalError; break;
case kAudioHardwareBadPropertySizeError:
errorText = "Audio Hardware: Bad Property Size";
result = paInternalError; break;
case kAudioHardwareIllegalOperationError:
errorText = "Audio Hardware: Illegal Operation";
result = paInternalError; break;
case kAudioHardwareBadDeviceError:
errorText = "Audio Hardware: Bad Device";
result = paInvalidDevice; break;
case kAudioHardwareBadStreamError:
errorText = "Audio Hardware: BadStream";
result = paBadStreamPtr; break;
case kAudioHardwareUnsupportedOperationError:
errorText = "Audio Hardware: Unsupported Operation";
result = paInternalError; break;
case kAudioDeviceUnsupportedFormatError:
errorText = "Audio Device: Unsupported Format";
result = paSampleFormatNotSupported; break;
case kAudioDevicePermissionsError:
errorText = "Audio Device: Permissions Error";
result = paDeviceUnavailable; break;
/* Audio Unit Errors: http://developer.apple.com/documentation/MusicAudio/Reference/CoreAudio/audio_units/chapter_5_section_3.html */
case kAudioUnitErr_InvalidProperty:
errorText = "Audio Unit: Invalid Property";
result = paInternalError; break;
case kAudioUnitErr_InvalidParameter:
errorText = "Audio Unit: Invalid Parameter";
result = paInternalError; break;
case kAudioUnitErr_NoConnection:
errorText = "Audio Unit: No Connection";
result = paInternalError; break;
case kAudioUnitErr_FailedInitialization:
errorText = "Audio Unit: Initialization Failed";
result = paInternalError; break;
case kAudioUnitErr_TooManyFramesToProcess:
errorText = "Audio Unit: Too Many Frames";
result = paInternalError; break;
case kAudioUnitErr_IllegalInstrument:
errorText = "Audio Unit: Illegal Instrument";
result = paInternalError; break;
case kAudioUnitErr_InstrumentTypeNotFound:
errorText = "Audio Unit: Instrument Type Not Found";
result = paInternalError; break;
case kAudioUnitErr_InvalidFile:
errorText = "Audio Unit: Invalid File";
result = paInternalError; break;
case kAudioUnitErr_UnknownFileType:
errorText = "Audio Unit: Unknown File Type";
result = paInternalError; break;
case kAudioUnitErr_FileNotSpecified:
errorText = "Audio Unit: File Not Specified";
result = paInternalError; break;
case kAudioUnitErr_FormatNotSupported:
errorText = "Audio Unit: Format Not Supported";
result = paInternalError; break;
case kAudioUnitErr_Uninitialized:
errorText = "Audio Unit: Unitialized";
result = paInternalError; break;
case kAudioUnitErr_InvalidScope:
errorText = "Audio Unit: Invalid Scope";
result = paInternalError; break;
case kAudioUnitErr_PropertyNotWritable:
errorText = "Audio Unit: PropertyNotWritable";
result = paInternalError; break;
case kAudioUnitErr_InvalidPropertyValue:
errorText = "Audio Unit: Invalid Property Value";
result = paInternalError; break;
case kAudioUnitErr_PropertyNotInUse:
errorText = "Audio Unit: Property Not In Use";
result = paInternalError; break;
case kAudioUnitErr_Initialized:
errorText = "Audio Unit: Initialized";
result = paInternalError; break;
case kAudioUnitErr_InvalidOfflineRender:
errorText = "Audio Unit: Invalid Offline Render";
result = paInternalError; break;
case kAudioUnitErr_Unauthorized:
errorText = "Audio Unit: Unauthorized";
result = paInternalError; break;
case kAudioUnitErr_CannotDoInCurrentContext:
errorText = "Audio Unit: cannot do in current context";
result = paInternalError; break;
default:
errorText = "Unknown Error";
result = paInternalError;
}
if (isError)
errorType = "Error";
else
errorType = "Warning";
if ((int)error < -99999 || (int)error > 99999)
DBUG(("%s on line %d: err='%4s', msg='%s'\n", errorType, line, (const char *)&error, errorText));
else
DBUG(("%s on line %d: err=%d, 0x%x, msg='%s'\n", errorType, line, (int)error, (unsigned)error, errorText));
PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText );
return result;
}
/*
* This function computes an appropriate ring buffer size given
* a requested latency (in seconds), sample rate and framesPerBuffer.
*
* The returned ringBufferSize is computed using the following
* constraints:
* - it must be at least 4.
* - it must be at least 3x framesPerBuffer.
* - it must be at least 2x the suggestedLatency.
* - it must be a power of 2.
* This function attempts to compute the minimum such size.
*
* FEEDBACK: too liberal/conservative/another way?
*/
static long computeRingBufferSize( const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
long inputFramesPerBuffer,
long outputFramesPerBuffer,
double sampleRate )
{
long ringSize;
int index;
int i;
double latencyTimesChannelCount ;
long framesPerBufferTimesChannelCount ;
VVDBUG(( "computeRingBufferSize()\n" ));
assert( inputParameters || outputParameters );
if( outputParameters && inputParameters )
{
latencyTimesChannelCount = MAX(
inputParameters->suggestedLatency * inputParameters->channelCount,
outputParameters->suggestedLatency * outputParameters->channelCount );
framesPerBufferTimesChannelCount = MAX(
inputFramesPerBuffer * inputParameters->channelCount,
outputFramesPerBuffer * outputParameters->channelCount );
}
else if( outputParameters )
{
latencyTimesChannelCount
= outputParameters->suggestedLatency * outputParameters->channelCount;
framesPerBufferTimesChannelCount
= outputFramesPerBuffer * outputParameters->channelCount;
}
else /* we have inputParameters */
{
latencyTimesChannelCount
= inputParameters->suggestedLatency * inputParameters->channelCount;
framesPerBufferTimesChannelCount
= inputFramesPerBuffer * inputParameters->channelCount;
}
ringSize = (long) ( latencyTimesChannelCount * sampleRate * 2 + .5);
VDBUG( ( "suggested latency * channelCount: %d\n", (int) (latencyTimesChannelCount*sampleRate) ) );
if( ringSize < framesPerBufferTimesChannelCount * 3 )
ringSize = framesPerBufferTimesChannelCount * 3 ;
VDBUG(("framesPerBuffer*channelCount:%d\n",(int)framesPerBufferTimesChannelCount));
VDBUG(("Ringbuffer size (1): %d\n", (int)ringSize ));
/* make sure it's at least 4 */
ringSize = MAX( ringSize, 4 );
/* round up to the next power of 2 */
index = -1;
for( i=0; i<sizeof(long)*8; ++i )
if( ringSize >> i & 0x01 )
index = i;
assert( index > 0 );
if( ringSize <= ( 0x01 << index ) )
ringSize = 0x01 << index ;
else
ringSize = 0x01 << ( index + 1 );
VDBUG(( "Final Ringbuffer size (2): %d\n", (int)ringSize ));
return ringSize;
}
/*
* Durring testing of core audio, I found that serious crashes could occur
* if properties such as sample rate were changed multiple times in rapid
* succession. The function below has some fancy logic to make sure that changes
* are acknowledged before another is requested. That seems to help a lot.
*/
#include <pthread.h>
typedef struct {
bool once; /* I didn't end up using this. bdr */
pthread_mutex_t mutex;
} MutexAndBool ;
static OSStatus propertyProc(
AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
void* inClientData )
{
MutexAndBool *mab = (MutexAndBool *) inClientData;
mab->once = TRUE;
pthread_mutex_unlock( &(mab->mutex) );
return noErr;
}
/* sets the value of the given property and waits for the change to
be acknowledged, and returns the final value, which is not guaranteed
by this function to be the same as the desired value. Obviously, this
function can only be used for data whose input and output are the
same size and format, and their size and format are known in advance.*/
PaError AudioDeviceSetPropertyNowAndWaitForChange(
AudioDeviceID inDevice,
UInt32 inChannel,
Boolean isInput,
AudioDevicePropertyID inPropertyID,
UInt32 inPropertyDataSize,
const void *inPropertyData,
void *outPropertyData )
{
OSStatus macErr;
int unixErr;
MutexAndBool mab;
UInt32 outPropertyDataSize = inPropertyDataSize;
/* First, see if it already has that value. If so, return. */
macErr = AudioDeviceGetProperty( inDevice, inChannel,
isInput, inPropertyID,
&outPropertyDataSize, outPropertyData );
if( macErr )
goto failMac2;
if( inPropertyDataSize!=outPropertyDataSize )
return paInternalError;
if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) )
return paNoError;
/* setup and lock mutex */
mab.once = FALSE;
unixErr = pthread_mutex_init( &mab.mutex, NULL );
if( unixErr )
goto failUnix2;
unixErr = pthread_mutex_lock( &mab.mutex );
if( unixErr )
goto failUnix;
/* add property listener */
macErr = AudioDeviceAddPropertyListener( inDevice, inChannel, isInput,
inPropertyID, propertyProc,
&mab );
if( macErr )
goto failMac;
/* set property */
macErr = AudioDeviceSetProperty( inDevice, NULL, inChannel,
isInput, inPropertyID,
inPropertyDataSize, inPropertyData );
if( macErr ) {
/* we couldn't set the property, so we'll just unlock the mutex
and move on. */
pthread_mutex_unlock( &mab.mutex );
}
/* wait for property to change */
unixErr = pthread_mutex_lock( &mab.mutex );
if( unixErr )
goto failUnix;
/* now read the property back out */
macErr = AudioDeviceGetProperty( inDevice, inChannel,
isInput, inPropertyID,
&outPropertyDataSize, outPropertyData );
if( macErr )
goto failMac;
/* cleanup */
AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
inPropertyID, propertyProc );
unixErr = pthread_mutex_unlock( &mab.mutex );
if( unixErr )
goto failUnix2;
unixErr = pthread_mutex_destroy( &mab.mutex );
if( unixErr )
goto failUnix2;
return paNoError;
failUnix:
pthread_mutex_destroy( &mab.mutex );
AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
inPropertyID, propertyProc );
failUnix2:
DBUG( ("Error #%d while setting a device property: %s\n", unixErr, strerror( unixErr ) ) );
return paUnanticipatedHostError;
failMac:
pthread_mutex_destroy( &mab.mutex );
AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
inPropertyID, propertyProc );
failMac2:
return ERR( macErr );
}
/*
* Sets the sample rate the HAL device.
* if requireExact: set the sample rate or fail.
*
* otherwise : set the exact sample rate.
* If that fails, check for available sample rates, and choose one
* higher than the requested rate. If there isn't a higher one,
* just use the highest available.
*/
static PaError setBestSampleRateForDevice( const AudioDeviceID device,
const bool isOutput,
const bool requireExact,
const Float64 desiredSrate )
{
/*FIXME: changing the sample rate is disruptive to other programs using the
device, so it might be good to offer a custom flag to not change the
sample rate and just do conversion. (in my casual tests, there is
no disruption unless the sample rate really does need to change) */
const bool isInput = isOutput ? 0 : 1;
Float64 srate;
UInt32 propsize = sizeof( Float64 );
OSErr err;
AudioValueRange *ranges;
int i=0;
Float64 max = -1; /*the maximum rate available*/
Float64 best = -1; /*the lowest sample rate still greater than desired rate*/
VDBUG(("Setting sample rate for device %ld to %g.\n",device,(float)desiredSrate));
/* -- try setting the sample rate -- */
err = AudioDeviceSetPropertyNowAndWaitForChange(
device, 0, isInput,
kAudioDevicePropertyNominalSampleRate,
propsize, &desiredSrate, &srate );
if( err )
return err;
/* -- if the rate agrees, and we got no errors, we are done -- */
if( !err && srate == desiredSrate )
return paNoError;
/* -- we've failed if the rates disagree and we are setting input -- */
if( requireExact )
return paInvalidSampleRate;
/* -- generate a list of available sample rates -- */
err = AudioDeviceGetPropertyInfo( device, 0, isInput,
kAudioDevicePropertyAvailableNominalSampleRates,
&propsize, NULL );
if( err )
return ERR( err );
ranges = (AudioValueRange *)calloc( 1, propsize );
if( !ranges )
return paInsufficientMemory;
err = AudioDeviceGetProperty( device, 0, isInput,
kAudioDevicePropertyAvailableNominalSampleRates,
&propsize, ranges );
if( err )
{
free( ranges );
return ERR( err );
}
VDBUG(("Requested sample rate of %g was not available.\n", (float)desiredSrate));
VDBUG(("%lu Available Sample Rates are:\n",propsize/sizeof(AudioValueRange)));
#ifdef MAC_CORE_VERBOSE_DEBUG
for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
VDBUG( ("\t%g-%g\n",
(float) ranges[i].mMinimum,
(float) ranges[i].mMaximum ) );
#endif
VDBUG(("-----\n"));
/* -- now pick the best available sample rate -- */
for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
{
if( ranges[i].mMaximum > max ) max = ranges[i].mMaximum;
if( ranges[i].mMinimum > desiredSrate ) {
if( best < 0 )
best = ranges[i].mMinimum;
else if( ranges[i].mMinimum < best )
best = ranges[i].mMinimum;
}
}
if( best < 0 )
best = max;
VDBUG( ("Maximum Rate %g. best is %g.\n", max, best ) );
free( ranges );
/* -- set the sample rate -- */
propsize = sizeof( best );
err = AudioDeviceSetPropertyNowAndWaitForChange(
device, 0, isInput,
kAudioDevicePropertyNominalSampleRate,
propsize, &best, &srate );
if( err )
return err;
if( err )
return ERR( err );
/* -- if the set rate matches, we are done -- */
if( srate == best )
return paNoError;
/* -- otherwise, something wierd happened: we didn't set the rate, and we got no errors. Just bail. */
return paInternalError;
}
/*
Attempts to set the requestedFramesPerBuffer. If it can't set the exact
value, it settles for something smaller if available. If nothing smaller
is available, it uses the smallest available size.
actualFramesPerBuffer will be set to the actual value on successful return.
OK to pass NULL to actualFramesPerBuffer.
The logic is very simmilar too setBestSampleRate only failure here is
not usually catastrophic.
*/
static PaError setBestFramesPerBuffer( const AudioDeviceID device,
const bool isOutput,
unsigned long requestedFramesPerBuffer,
unsigned long *actualFramesPerBuffer )
{
UInt32 afpb;
const bool isInput = !isOutput;
UInt32 propsize = sizeof(UInt32);
OSErr err;
Float64 min = -1; /*the min blocksize*/
Float64 best = -1; /*the best blocksize*/
int i=0;
AudioValueRange *ranges;
if( actualFramesPerBuffer == NULL )
actualFramesPerBuffer = &afpb;
/* -- try and set exact FPB -- */
err = AudioDeviceSetProperty( device, NULL, 0, isInput,
kAudioDevicePropertyBufferFrameSize,
propsize, &requestedFramesPerBuffer);
err = AudioDeviceGetProperty( device, 0, isInput,
kAudioDevicePropertyBufferFrameSize,
&propsize, actualFramesPerBuffer);
if( err )
return ERR( err );
if( *actualFramesPerBuffer == requestedFramesPerBuffer )
return paNoError; /* we are done */
/* -- fetch available block sizes -- */
err = AudioDeviceGetPropertyInfo( device, 0, isInput,
kAudioDevicePropertyBufferSizeRange,
&propsize, NULL );
if( err )
return ERR( err );
ranges = (AudioValueRange *)calloc( 1, propsize );
if( !ranges )
return paInsufficientMemory;
err = AudioDeviceGetProperty( device, 0, isInput,
kAudioDevicePropertyBufferSizeRange,
&propsize, ranges );
if( err )
{
free( ranges );
return ERR( err );
}
VDBUG(("Requested block size of %lu was not available.\n",
requestedFramesPerBuffer ));
VDBUG(("%lu Available block sizes are:\n",propsize/sizeof(AudioValueRange)));
#ifdef MAC_CORE_VERBOSE_DEBUG
for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
VDBUG( ("\t%g-%g\n",
(float) ranges[i].mMinimum,
(float) ranges[i].mMaximum ) );
#endif
VDBUG(("-----\n"));
/* --- now pick the best available framesPerBuffer -- */
for( i=0; i<propsize/sizeof(AudioValueRange); ++i )
{
if( min == -1 || ranges[i].mMinimum < min ) min = ranges[i].mMinimum;
if( ranges[i].mMaximum < requestedFramesPerBuffer ) {
if( best < 0 )
best = ranges[i].mMaximum;
else if( ranges[i].mMaximum > best )
best = ranges[i].mMaximum;
}
}
if( best == -1 )
best = min;
VDBUG( ("Minimum FPB %g. best is %g.\n", min, best ) );
free( ranges );
/* --- set the buffer size (ignore errors) -- */
requestedFramesPerBuffer = (UInt32) best ;
propsize = sizeof( UInt32 );
err = AudioDeviceSetProperty( device, NULL, 0, isInput,
kAudioDevicePropertyBufferSize,
propsize, &requestedFramesPerBuffer );
/* --- read the property to check that it was set -- */
err = AudioDeviceGetProperty( device, 0, isInput,
kAudioDevicePropertyBufferSize,
&propsize, actualFramesPerBuffer );
if( err )
return ERR( err );
return paNoError;
}

View file

@ -1,53 +0,0 @@
# Make PortAudio for Silicon Graphics IRIX 6.2 - 6.5.
# Pieter suurmond, july 4, 2002. (for PA v18.)
# 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 (on SGI Indy with R5000 @ 180MHz running IRIX 6.2):
#TESTC = $(PASRC) ../pa_tests/patest_record.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_many.c # v18 ok (with PA_MIN_LATENCY_MSEC=250).
#TESTC = $(PASRC) ../pa_tests/patest_latency.c # v18 ok (with PA_MIN_LATENCY_MSEC=250) patest_latency.c contained //
#TESTC = $(PASRC) ../pa_tests/patest_longsine.c # v18 ok (with PA_MIN_LATENCY_MSEC=250).
TESTC = $(PASRC) ../pa_tests/patest_saw.c # v18 ok (with PA_MIN_LATENCY_MSEC=250).
#TESTC = $(PASRC) ../pa_tests/patest_wire.c # v18 seems ok.
#TESTC = $(PASRC) ../pa_tests/pa_devs.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_sine.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_sine8.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_pink.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_clip.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_stop.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_dither.c # TODO probably ok.
#TESTC = $(PASRC) ../pa_tests/patest_sync.c # BEEPS and delta's, no crashes anymore with v15.
# 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

File diff suppressed because it is too large Load diff

View file

@ -1,52 +0,0 @@
# 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

@ -1,908 +0,0 @@
/*
* 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;
}

60
pa_tests/README.txt Normal file
View file

@ -0,0 +1,60 @@
This directory contains various programs to test PortAudio. The files
named patest_* are tests, the files named debug_* are just scratch
files that may or may not work.
All following tests are up to date with the V19 API. They should all compile
(without any warnings on GCC 3.3). Note that this does not necissarily mean that
the tests pass, just that they compile.
x- paqa_devs.c
x- paqa_errs.c (needs reviewing)
x- patest1.c
x- patest_buffer.c
x- patest_callbackstop.c
x- patest_clip.c (last test fails, dither doesn't currently force clip in V19)
x- patest_dither.c
x- patest_hang.c
x- patest_latency.c
x- patest_leftright.c
x- patest_longsine.c
x- patest_many.c
x- patest_maxsines.c
o- patest_mono.c
x- patest_multi_sine.c
x- patest_pink.c
x- patest_prime.c
x- patest_read_record.c
x- patest_record.c
x- patest_ringmix.c
x- patest_saw.c
x- patest_sine.c
x- patest_sine8.c
x- patest_sine_formats.c
x- patest_sine_time.c
x- patest_start_stop.c
x- patest_stop.c
x- patest_sync.c
x- patest_toomanysines.c
o- patest_two_rates.c
x- patest_underflow.c
x- patest_wire.c
x- patest_write_sine.c
x- pa_devs.c
x- pa_fuzz.c
x- pa_minlat.c
The debug_ files are still in V18 format and may need some V19 adaption.
Feel free to fix them, most simply require adjusting to the new API.
o- pa_tests/debug_convert.c
o- pa_tests/debug_dither_calc.c
o- pa_tests/debug_dual.c
o- pa_tests/debug_multi_in.c
o- pa_tests/debug_multi_out.c
o- pa_tests/debug_record.c
o- pa_tests/debug_record_reuse.c
o- pa_tests/debug_sine.c
o- pa_tests/debug_sine_amp.c
o- pa_tests/debug_sine_formats.c
o- pa_tests/debug_srate.c
o- pa_tests/debug_test1.c

View file

@ -1,9 +1,12 @@
/** @file pa_devs.c
@brief List available devices, including device information.
@author Phil Burk http://www.softsynth.com
@note Define PA_NO_ASIO to compile this code on Windows without
ASIO support.
*/
/*
* $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
@ -36,59 +39,188 @@
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#ifdef WIN32
#ifndef PA_NO_ASIO
#include "pa_asio.h"
#endif
#endif
/*******************************************************************/
static void PrintSupportedStandardSampleRates(
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters )
{
static double standardSampleRates[] = {
8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated list */
};
int i, printCount;
PaError err;
printCount = 0;
for( i=0; standardSampleRates[i] > 0; i++ )
{
err = Pa_IsFormatSupported( inputParameters, outputParameters, standardSampleRates[i] );
if( err == paFormatIsSupported )
{
if( printCount == 0 )
{
printf( "\t%8.2f", standardSampleRates[i] );
printCount = 1;
}
else if( printCount == 4 )
{
printf( ",\n\t%8.2f", standardSampleRates[i] );
printCount = 1;
}
else
{
printf( ", %8.2f", standardSampleRates[i] );
++printCount;
}
}
}
if( !printCount )
printf( "None\n" );
else
printf( "\n" );
}
/*******************************************************************/
int main(void);
int main(void)
{
int i,j;
int numDevices;
const PaDeviceInfo *pdi;
PaError err;
int i, numDevices, defaultDisplayed;
const PaDeviceInfo *deviceInfo;
PaStreamParameters inputParameters, outputParameters;
PaError err;
Pa_Initialize();
numDevices = Pa_CountDevices();
printf( "PortAudio version number = %d\nPortAudio version text = '%s'\n",
Pa_GetVersion(), Pa_GetVersionText() );
numDevices = Pa_GetDeviceCount();
if( numDevices < 0 )
{
printf("ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
printf( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices );
err = numDevices;
goto error;
}
printf("Number of devices = %d\n", numDevices );
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 )
deviceInfo = Pa_GetDeviceInfo( i );
printf( "--------------------------------------- device #%d\n", i );
/* Mark global and API specific default devices */
defaultDisplayed = 0;
if( i == Pa_GetDefaultInputDevice() )
{
printf("Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] );
printf( "[ Default Input" );
defaultDisplayed = 1;
}
else
else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultInputDevice )
{
printf("Sample Rates =");
for( j=0; j<pdi->numSampleRates; j++ )
{
printf(" %8.2f,", pdi->sampleRates[j] );
}
printf("\n");
const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
printf( "[ Default %s Input", hostInfo->name );
defaultDisplayed = 1;
}
if( i == Pa_GetDefaultOutputDevice() )
{
printf( (defaultDisplayed ? "," : "[") );
printf( " Default Output" );
defaultDisplayed = 1;
}
else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice )
{
const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
printf( (defaultDisplayed ? "," : "[") );
printf( " Default %s Output", hostInfo->name );
defaultDisplayed = 1;
}
if( defaultDisplayed )
printf( " ]\n" );
/* print device info fields */
printf( "Name = %s\n", deviceInfo->name );
printf( "Host API = %s\n", Pa_GetHostApiInfo( deviceInfo->hostApi )->name );
printf( "Max inputs = %d", deviceInfo->maxInputChannels );
printf( ", Max outputs = %d\n", deviceInfo->maxOutputChannels );
printf( "Default low input latency = %8.3f\n", deviceInfo->defaultLowInputLatency );
printf( "Default low output latency = %8.3f\n", deviceInfo->defaultLowOutputLatency );
printf( "Default high input latency = %8.3f\n", deviceInfo->defaultHighInputLatency );
printf( "Default high output latency = %8.3f\n", deviceInfo->defaultHighOutputLatency );
#ifdef WIN32
#ifndef PA_NO_ASIO
/* ASIO specific latency information */
if( Pa_GetHostApiInfo( deviceInfo->hostApi )->type == paASIO ){
long minLatency, maxLatency, preferredLatency, granularity;
err = PaAsio_GetAvailableLatencyValues( i,
&minLatency, &maxLatency, &preferredLatency, &granularity );
printf( "ASIO minimum buffer size = %ld\n", minLatency );
printf( "ASIO maximum buffer size = %ld\n", maxLatency );
printf( "ASIO preferred buffer size = %ld\n", preferredLatency );
if( granularity == -1 )
printf( "ASIO buffer granularity = power of 2\n" );
else
printf( "ASIO buffer granularity = %ld\n", granularity );
}
#endif /* !PA_NO_ASIO */
#endif /* WIN32 */
printf( "Default sample rate = %8.2f\n", deviceInfo->defaultSampleRate );
/* poll for standard sample rates */
inputParameters.device = i;
inputParameters.channelCount = deviceInfo->maxInputChannels;
inputParameters.sampleFormat = paInt16;
inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
inputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.device = i;
outputParameters.channelCount = deviceInfo->maxOutputChannels;
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
outputParameters.hostApiSpecificStreamInfo = NULL;
if( inputParameters.channelCount > 0 )
{
printf("Supported standard sample rates\n for half-duplex 16 bit %d channel input = \n",
inputParameters.channelCount );
PrintSupportedStandardSampleRates( &inputParameters, NULL );
}
if( outputParameters.channelCount > 0 )
{
printf("Supported standard sample rates\n for half-duplex 16 bit %d channel output = \n",
outputParameters.channelCount );
PrintSupportedStandardSampleRates( NULL, &outputParameters );
}
if( inputParameters.channelCount > 0 && outputParameters.channelCount > 0 )
{
printf("Supported standard sample rates\n for full-duplex 16 bit %d channel input, %d channel output = \n",
inputParameters.channelCount, outputParameters.channelCount );
PrintSupportedStandardSampleRates( &inputParameters, &outputParameters );
}
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" );

View file

@ -1,9 +1,9 @@
/** @file pa_fuzz.c
@brief Distort input like a fuzz boz.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -48,9 +48,11 @@
typedef float SAMPLE;
float CubicAmplifier( float input );
static int fuzzCallback( void *inputBuffer, void *outputBuffer,
static int fuzzCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData );
/* Non-linear amplifier with soft distortion curve. */
float CubicAmplifier( float input )
@ -76,14 +78,17 @@ static int gNumNoInputs = 0;
** 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,
static int fuzzCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
SAMPLE *out = (SAMPLE*)outputBuffer;
SAMPLE *in = (SAMPLE*)inputBuffer;
const SAMPLE *in = (const SAMPLE*)inputBuffer;
unsigned int i;
(void) outTime; /* Prevent unused variable warnings. */
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) userData;
if( inputBuffer == NULL )
@ -103,33 +108,41 @@ static int fuzzCallback( void *inputBuffer, void *outputBuffer,
*out++ = *in++; /* right - clean */
}
}
return 0;
return paContinue;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStreamParameters inputParameters, outputParameters;
PaStream *stream;
PaError err;
err = Pa_Initialize();
if( err != paNoError ) goto error;
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
inputParameters.channelCount = 2; /* stereo input */
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
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,
&inputParameters,
&outputParameters,
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 */
0, /* paClipOff, */ /* we won't output out of range samples so don't bother clipping them */
fuzzCallback,
NULL );
if( err != paNoError ) goto error;

View file

@ -1,10 +1,10 @@
/** @file pa_minlat.c
@brief Experiment with different numbers of buffers to determine the
minimum latency for a computer.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -34,9 +34,11 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "portaudio.h"
#ifndef M_PI
@ -44,7 +46,7 @@
#endif
#define TWOPI (M_PI * 2.0)
#define DEFAULT_BUFFER_SIZE (64)
#define DEFAULT_BUFFER_SIZE (32)
typedef struct
{
@ -54,9 +56,11 @@ typedef struct
paTestData;
/* Very simple synthesis routine to generate two sine waves. */
static int paminlatCallback( void *inputBuffer, void *outputBuffer,
static int paminlatCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
@ -82,75 +86,85 @@ static int paminlatCallback( void *inputBuffer, void *outputBuffer,
data->right_phase = right_phase;
return 0;
}
void main( int argc, char **argv );
void main( int argc, char **argv )
int main( int argc, char **argv );
int main( int argc, char **argv )
{
PortAudioStream *stream;
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
paTestData data;
int go;
int numBuffers = 0;
int minBuffers = 0;
int outLatency = 0;
int minLatency = DEFAULT_BUFFER_SIZE * 2;
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("pa_minlat - Determine minimum latency for your computer.\n");
printf(" usage: pa_minlat {userBufferSize}\n");
printf(" for example: pa_minlat 64\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("Then try to find the smallest number of frames 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 );
outLatency = sampleRate * 200.0 / 1000.0; /* 200 msec. */
/* Try different numBuffers in a loop. */
go = 1;
while( go )
{
outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */
outputParameters.channelCount = 2; /* Stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
outputParameters.suggestedLatency = (double)outLatency / sampleRate; /* In seconds. */
outputParameters.hostApiSpecificStreamInfo = NULL;
printf("Latency = %d frames = %6.1f msec.\n", outLatency, outputParameters.suggestedLatency * 1000.0 );
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,
NULL, /* no input */
&outputParameters,
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. */
/* Ask user for a new nlatency. */
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 );
printf("Latency now %d, enter new number of frames, or 'q' to quit: ", outLatency );
fgets( str, 256, stdin );
{
/* Get rid of newline */
size_t l = strlen( str ) - 1;
if( str[ l ] == '\n')
str[ l ] = '\0';
}
if( str[0] == 'q' ) go = 0;
else
{
numBuffers = atol( str );
if( numBuffers < minBuffers )
outLatency = atol( str );
if( outLatency < minLatency )
{
printf( "numBuffers below minimum of %d! Set to minimum!!!\n", minBuffers );
numBuffers = minBuffers;
printf( "Latency below minimum of %d! Set to minimum!!!\n", minLatency );
outLatency = minLatency;
}
}
/* Stop sound until ENTER hit. */
@ -163,10 +177,11 @@ void main( int argc, char **argv )
printf("the minimum latency that worked.\n");
printf("PortAudio: Test finished.\n");
Pa_Terminate();
return;
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

@ -1,11 +1,16 @@
/** @file paqa_devs.c
@brief 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
Pieter adapted to V19 API. Test now relies heavily on
Pa_IsFormatSupported(). Uses same 'standard' sample rates
as in test pa_devs.c.
*/
/*
* $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
@ -39,9 +44,11 @@
#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;
@ -52,18 +59,23 @@ typedef struct PaQaData
PaSampleFormat format;
}
PaQaData;
/****************************************** Prototypes ***********/
static void TestDevices( int mode );
static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
static void TestFormats( int mode, PaDeviceIndex deviceID, double sampleRate,
int numChannels );
static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate,
int numChannels, PaSampleFormat format );
static int QaCallback( void *inputBuffer, void *outputBuffer,
static int QaCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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. */
@ -82,20 +94,22 @@ static int gNumFailed = 0;
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,
static int QaCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
unsigned long i;
short phase;
PaQaData *data = (PaQaData *) userData;
(void) inputBuffer;
(void) outTime;
/* Play simle sawtooth wave. */
if( data->mode == MODE_OUTPUT )
@ -163,17 +177,18 @@ static int QaCallback( void *inputBuffer, void *outputBuffer,
/* Are we through yet? */
if( data->framesLeft > framesPerBuffer )
{
AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
PaUtil_AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft );
data->framesLeft -= framesPerBuffer;
return 0;
}
else
{
AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft );
PaUtil_AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft );
data->framesLeft = 0;
return 1;
}
}
/*******************************************************************/
int main(void);
int main(void)
@ -187,69 +202,60 @@ int main(void)
error:
Pa_Terminate();
printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
return 0;
}
/*******************************************************************
* Try each output device, through its full range of capabilities. */
static void TestDevices( int mode )
{
int id,jc,kr;
int id, jc, i;
int maxChannels;
const PaDeviceInfo *pdi;
int numDevices = Pa_CountDevices();
/* Iterate through all devices. */
for( id=0; id<numDevices; id++ )
static double standardSampleRates[] = { 8000.0, 9600.0, 11025.0, 12000.0,
16000.0, 22050.0, 24000.0,
32000.0, 44100.0, 48000.0,
88200.0, 96000.0,
-1.0 }; /* Negative terminated list. */
int numDevices = Pa_GetDeviceCount();
for( id=0; id<numDevices; id++ ) /* Iterate through all devices. */
{
pdi = Pa_GetDeviceInfo( id );
/* Try 1 to maxChannels on each device. */
maxChannels = ( mode == MODE_INPUT ) ? pdi->maxInputChannels : pdi->maxOutputChannels;
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 )
/* Try each standard sample rate. */
for( i=0; standardSampleRates[i] > 0; i++ )
{
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 );
}
TestFormats( mode, (PaDeviceIndex)id, standardSampleRates[i], jc );
}
}
}
}
/*******************************************************************/
static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate,
static void TestFormats( int mode, PaDeviceIndex 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, 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,
static int TestAdvance( int mode, PaDeviceIndex deviceID, double sampleRate,
int numChannels, PaSampleFormat format )
{
PortAudioStream *stream = NULL;
PaError result;
PaStreamParameters inputParameters, outputParameters, *ipp, *opp;
PaStream *stream = NULL;
PaError result = paNoError;
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);
#define FRAMES_PER_BUFFER (64)
/* Setup data for synthesis thread. */
myData.framesLeft = (unsigned long) (sampleRate * 100); /* 100 seconds */
myData.numChannels = numChannels;
@ -262,54 +268,78 @@ static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate,
case paInt24:
myData.bytesPerSample = 4;
break;
case paPackedInt24:
/* case paPackedInt24:
myData.bytesPerSample = 3;
break;
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 )
if( mode == MODE_INPUT )
{
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;
inputParameters.device = deviceID;
inputParameters.channelCount = numChannels;
inputParameters.sampleFormat = format;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
ipp = &inputParameters;
}
else
ipp = NULL;
if( mode == MODE_OUTPUT ) /* Pa_GetDeviceInfo(paNoDevice) COREDUMPS!!! */
{
outputParameters.device = deviceID;
outputParameters.channelCount = numChannels;
outputParameters.sampleFormat = format;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
opp = &outputParameters;
}
else
opp = NULL;
if(paFormatIsSupported == Pa_IsFormatSupported( ipp, opp, sampleRate ))
{
printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %lu -------\n",
( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT",
deviceID, sampleRate, numChannels, (unsigned long)format);
EXPECT( ((result = Pa_OpenStream( &stream,
ipp,
opp,
sampleRate,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
QaCallback,
&myData ) ) == 0) );
if( stream )
{
PaTime oldStamp, newStamp;
unsigned long oldFrames;
int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400;
/* Was:
int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate );
int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate);
*/
int msec = (int)( 3.0 *
(( mode == MODE_INPUT ) ? inputParameters.suggestedLatency : outputParameters.suggestedLatency ));
if( msec < minDelay ) msec = minDelay;
printf("msec = %d\n", msec); /**/
EXPECT( ((result=Pa_StartStream( stream )) == 0) );
/* Check to make sure PortAudio is advancing timeStamp. */
oldStamp = Pa_GetStreamTime(stream);
Pa_Sleep(msec);
newStamp = Pa_GetStreamTime(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 = %lu, myData.framesLeft = %lu\n", oldFrames, myData.framesLeft ); /**/
EXPECT( (oldFrames > myData.framesLeft) );
EXPECT( ((result=Pa_CloseStream( stream )) == 0) );
stream = NULL;
}
}
error:
if( stream != NULL ) Pa_CloseStream( stream );

View file

@ -1,10 +1,11 @@
/** @file paqa_errs.c
@brief Self Testing Quality Assurance app for PortAudio
Do lots of bad things to test error reporting.
@author Phil Burk http://www.softsynth.com
Pieter Suurmond adapted to V19 API.
*/
/*
* $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
@ -34,15 +35,18 @@
* 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)
/*--------- Definitions ---------*/
#define MODE_INPUT (0)
#define MODE_OUTPUT (1)
#define FRAMES_PER_BUFFER (64)
#define SAMPLE_RATE (44100.0)
typedef struct PaQaData
{
unsigned long framesLeft;
@ -51,21 +55,14 @@ typedef struct PaQaData
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 gNumPassed = 0; /* Two globals */
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. */
/*------------------- 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 \
{ \
@ -78,6 +75,7 @@ static int gNumFailed = 0;
goto error; \
} \
} while(0)
#define HOPEFOR(_exp) \
do \
{ \
@ -89,20 +87,24 @@ static int gNumFailed = 0;
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().
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 )
static int QaCallback( const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData )
{
unsigned long i;
unsigned char *out = (unsigned char *) outputBuffer;
PaQaData *data = (PaQaData *) userData;
(void) inputBuffer; /* Prevent "unused variable" warnings. */
(void) outTime;
unsigned long i;
unsigned char* out = (unsigned char *) outputBuffer;
PaQaData* data = (PaQaData *) userData;
(void)inputBuffer; /* Prevent "unused variable" warnings. */
/* Zero out buffer so we don't hear terrible noise. */
if( data->mode == MODE_OUTPUT )
@ -125,206 +127,254 @@ static int QaCallback( void *inputBuffer, void *outputBuffer,
return 1;
}
}
/*******************************************************************/
static PaDeviceIndex FindInputOnlyDevice(void)
{
PaDeviceIndex result = Pa_GetDefaultInputDevice();
if( result != paNoDevice && Pa_GetDeviceInfo(result)->maxOutputChannels == 0 )
return result;
for( result = 0; result < Pa_GetDeviceCount(); ++result )
{
if( Pa_GetDeviceInfo(result)->maxOutputChannels == 0 )
return result;
}
return paNoDevice;
}
static PaDeviceIndex FindOutputOnlyDevice(void)
{
PaDeviceIndex result = Pa_GetDefaultOutputDevice();
if( result != paNoDevice && Pa_GetDeviceInfo(result)->maxInputChannels == 0 )
return result;
for( result = 0; result < Pa_GetDeviceCount(); ++result )
{
if( Pa_GetDeviceInfo(result)->maxInputChannels == 0 )
return result;
}
return paNoDevice;
}
/*-------------------------------------------------------------------------------------------------*/
static int TestBadOpens( void )
{
PaStream* stream = NULL;
PaError result;
PaQaData myData;
PaStreamParameters ipp, opp;
/* Setup data for synthesis thread. */
myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */
myData.numChannels = 1;
myData.mode = MODE_OUTPUT;
/*----------------------------- No devices specified: */
ipp.device = opp.device = paNoDevice;
ipp.channelCount = opp.channelCount = 0; /* Also no channels. */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
/* Take the low latency of the default device for all subsequent tests. */
ipp.suggestedLatency = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice())->defaultLowInputLatency;
opp.suggestedLatency = Pa_GetDeviceInfo(Pa_GetDefaultOutputDevice())->defaultLowOutputLatency;
HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidDevice));
/*----------------------------- No devices specified #2: */
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidDevice));
/*----------------------------- Out of range input device specified: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = Pa_GetDeviceCount(); /* And no output device, and no channels. */
opp.channelCount = 0; opp.device = paNoDevice;
HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidDevice));
/*----------------------------- Out of range output device specified: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no channels. */
opp.channelCount = 0; opp.device = Pa_GetDeviceCount();
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidDevice));
/*----------------------------- Zero input channels: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = Pa_GetDefaultInputDevice();
opp.channelCount = 0; opp.device = paNoDevice; /* And no output device, and no output channels. */
HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidChannelCount));
/*----------------------------- Zero output channels: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no input channels. */
opp.channelCount = 0; opp.device = Pa_GetDefaultOutputDevice();
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidChannelCount));
/*----------------------------- Nonzero input and output channels but no output device: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 2; ipp.device = Pa_GetDefaultInputDevice(); /* Both stereo. */
opp.channelCount = 2; opp.device = paNoDevice;
HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidDevice));
/*----------------------------- Nonzero input and output channels but no input device: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 2; ipp.device = paNoDevice;
opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice();
HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidDevice));
/*----------------------------- NULL stream pointer: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = paNoDevice; /* Output is more likely than input. */
opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice(); /* Only 2 output channels. */
HOPEFOR(((result = Pa_OpenStream(NULL, &ipp, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paBadStreamPtr));
/*----------------------------- Low sample rate: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = paNoDevice;
opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice();
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
1.0, FRAMES_PER_BUFFER, /* 1 cycle per second (1 Hz) is too low. */
paClipOff, QaCallback, &myData )) == paInvalidSampleRate));
/*----------------------------- High sample rate: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = paNoDevice;
opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice();
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
10000000.0, FRAMES_PER_BUFFER, /* 10^6 cycles per second (10 MHz) is too high. */
paClipOff, QaCallback, &myData )) == paInvalidSampleRate));
/*----------------------------- NULL callback: */
/* NULL callback is valid in V19 -- it means use blocking read/write stream
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = paNoDevice;
opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice();
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff,
NULL,
&myData )) == paNullCallback));
*/
/*----------------------------- Bad flag: */
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = paNoDevice;
opp.channelCount = 2; opp.device = Pa_GetDefaultOutputDevice();
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
255, /* Is 8 maybe legal V19 API? */
QaCallback, &myData )) == paInvalidFlag));
/*----------------------------- using input device as output device: */
if( FindInputOnlyDevice() != paNoDevice )
{
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 0; ipp.device = paNoDevice; /* And no input device, and no channels. */
opp.channelCount = 2; opp.device = FindInputOnlyDevice();
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, &opp,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidChannelCount));
}
/*----------------------------- using output device as input device: */
if( FindOutputOnlyDevice() != paNoDevice )
{
ipp.hostApiSpecificStreamInfo = opp.hostApiSpecificStreamInfo = NULL;
ipp.sampleFormat = opp.sampleFormat = paFloat32;
ipp.channelCount = 2; ipp.device = FindOutputOnlyDevice();
opp.channelCount = 0; opp.device = paNoDevice; /* And no output device, and no channels. */
HOPEFOR(((result = Pa_OpenStream(&stream, &ipp, NULL,
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paInvalidChannelCount));
}
if( stream != NULL ) Pa_CloseStream( stream );
return result;
}
/*-----------------------------------------------------------------------------------------*/
static int TestBadActions( void )
{
PaStream* stream = NULL;
PaError result;
PaQaData myData;
PaStreamParameters opp;
/* Setup data for synthesis thread. */
myData.framesLeft = (unsigned long)(SAMPLE_RATE * 100); /* 100 seconds */
myData.numChannels = 1;
myData.mode = MODE_OUTPUT;
opp.device = Pa_GetDefaultOutputDevice(); /* Default output. */
opp.channelCount = 2; /* Stereo output. */
opp.hostApiSpecificStreamInfo = NULL;
opp.sampleFormat = paFloat32;
opp.suggestedLatency = Pa_GetDeviceInfo(opp.device)->defaultLowOutputLatency;
HOPEFOR(((result = Pa_OpenStream(&stream, NULL, /* Take NULL as input parame- */
&opp, /* ters, meaning try only output. */
SAMPLE_RATE, FRAMES_PER_BUFFER,
paClipOff, QaCallback, &myData )) == paNoError));
HOPEFOR(((result = Pa_StartStream(NULL)) == paBadStreamPtr));
HOPEFOR(((result = Pa_StopStream(NULL)) == paBadStreamPtr));
HOPEFOR(((result = Pa_IsStreamStopped(NULL)) == paBadStreamPtr));
HOPEFOR(((result = Pa_IsStreamActive(NULL)) == paBadStreamPtr));
HOPEFOR(((result = Pa_CloseStream(NULL)) == paBadStreamPtr));
HOPEFOR(((result = Pa_SetStreamFinishedCallback(NULL, NULL)) == paBadStreamPtr));
HOPEFOR(((result = !Pa_GetStreamInfo(NULL))));
HOPEFOR(((result = Pa_GetStreamTime(NULL)) == 0.0));
HOPEFOR(((result = Pa_GetStreamCpuLoad(NULL)) == 0.0));
HOPEFOR(((result = Pa_ReadStream(NULL, NULL, 0)) == paBadStreamPtr));
HOPEFOR(((result = Pa_WriteStream(NULL, NULL, 0)) == paBadStreamPtr));
/** @todo test Pa_GetStreamReadAvailable and Pa_GetStreamWriteAvailable */
if (stream != NULL) Pa_CloseStream(stream);
return result;
}
/*---------------------------------------------------------------------*/
int main(void);
int main(void)
{
PaError result;
EXPECT( ((result=Pa_Initialize()) == 0) );
EXPECT(((result = Pa_Initialize()) == paNoError));
TestBadOpens();
TestBadActions();
error:
Pa_Terminate();
printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed );
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

@ -1,18 +1,48 @@
/*
$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.
/** @file patest1.c
@brief Ring modulate the audio input with a sine wave for 20 seconds.
@author Ross Bencina <rossb@audiomulch.com>
*/
/*
* $Id$
*
* 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"
#ifndef M_PI
#define M_PI (3.14159265)
#define M_PI (3.14159265)
#endif
#define SAMPLE_RATE (44100)
typedef struct
{
float sine[100];
@ -20,95 +50,143 @@ typedef struct
int sampsToGo;
}
patest1data;
static int patest1Callback( void *inputBuffer, void *outputBuffer,
unsigned long bufferFrames,
PaTimestamp outTime, void *userData )
static int patest1Callback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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 )
int framesToCalc = framesPerBuffer;
unsigned long i = 0;
int finished;
if( data->sampsToGo < framesPerBuffer )
{
framesToCalc = data->sampsToGo;
finished = 1;
finished = paComplete;
}
for( i=0; i<framesToCalc; i++ )
else
{
finished = paContinue;
}
for( ; 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++ )
for( ; i<framesPerBuffer; 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();
PaStream *stream;
PaError err;
patest1data data;
int i;
PaStreamParameters inputParameters, outputParameters;
const PaHostErrorInfo* herr;
printf("patest1.c\n"); fflush(stdout);
printf("Ring modulate input for 20 seconds.\n"); fflush(stdout);
/* 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
data.sampsToGo = SAMPLE_RATE * 20; /* 20 seconds. */
/* initialise portaudio subsytem */
Pa_Initialize();
err = Pa_Initialize();
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
inputParameters.channelCount = 2; /* stereo input */
inputParameters.sampleFormat = paFloat32; /* 32 bit floating point input */
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
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 );
&stream,
&inputParameters,
&outputParameters,
(double)SAMPLE_RATE, /* Samplerate in Hertz. */
512, /* Small buffers */
paClipOff, /* We won't output out of range samples so don't bother clipping them. */
patest1Callback,
&data );
if( err != paNoError ) goto done;
printf( "Waiting for stream to complete...\n" );
err = Pa_StartStream( stream );
if( err != paNoError ) goto done;
printf( "Press any key to end.\n" ); fflush(stdout);
getc( stdin ); /* wait for input before exiting */
while( Pa_StreamActive( stream ) )
Pa_Sleep(1000); /* sleep until playback has finished */
err = Pa_AbortStream( stream );
if( err != paNoError ) goto done;
printf( "Waiting for stream to complete...\n" );
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 );
}
/* sleep until playback has finished */
while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(1000);
if( err < 0 ) goto done;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto done;
done:
Pa_Terminate();
if( err != paNoError )
{
fprintf( stderr, "An error occured while using portaudio\n" );
if( err == paUnanticipatedHostError )
{
fprintf( stderr, " unanticipated host error.\n");
herr = Pa_GetLastHostErrorInfo();
if (herr)
{
fprintf( stderr, " Error number: %ld\n", herr->errorCode );
if (herr->errorText)
fprintf( stderr, " Error text: %s\n", herr->errorText );
}
else
fprintf( stderr, " Pa_GetLastHostErrorInfo() failed!\n" );
}
else
{
fprintf( stderr, " Error number: %d\n", err );
fprintf( stderr, " Error text: %s\n", Pa_GetErrorText( err ) );
}
err = 1; /* Always return 0 or 1, but no other return codes. */
}
printf( "bye\n" );
return 0;
return err;
}

View file

@ -1,9 +1,9 @@
/** @file patest_buffer.c
@brief Test opening streams with different buffer sizes.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -37,15 +37,15 @@
#include <stdlib.h>
#include <math.h>
#include "portaudio.h"
#define NUM_SECONDS (1)
#define NUM_SECONDS (3)
#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};
#define BUFFER_TABLE 14
long buffer_table[] = {paFramesPerBufferUnspecified,16,32,64,128,200,256,500,512,600,723,1000,1024,2345};
typedef struct
{
@ -55,25 +55,23 @@ typedef struct
unsigned int sampsToGo;
}
paTestData;
PaError TestOnce( int buffersize );
PaError TestOnce( int buffersize, PaDeviceIndex );
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,
static int patest1Callback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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 )
{
@ -112,27 +110,37 @@ static int patest1Callback( void *inputBuffer, void *outputBuffer,
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
int main(int argc, char **args);
int main(int argc, char **args)
{
int i;
int device = -1;
PaError err;
printf("Test opening streams with different buffer sizes\n\n");
printf("Test opening streams with different buffer sizes\n");
if( argc > 1 ) {
device=atoi( args[1] );
printf("Using device number %d.\n\n", device );
} else {
printf("Using default device.\n\n" );
}
for (i = 0 ; i < BUFFER_TABLE; i++)
{
printf("Buffer size %d\n", buffer_table[i]);
err = TestOnce(buffer_table[i]);
printf("Buffer size %ld\n", buffer_table[i]);
err = TestOnce(buffer_table[i], device);
if( err < 0 ) return 0;
}
return 0;
}
PaError TestOnce( int buffersize )
PaError TestOnce( int buffersize, PaDeviceIndex device )
{
PortAudioStream *stream;
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
paTestData data;
int i;
@ -146,20 +154,22 @@ PaError TestOnce( int buffersize )
data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
if( device == -1 )
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
else
outputParameters.device = device ;
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paInt16; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
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,
NULL, /* no input */
&outputParameters,
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 */
buffersize, /* frames per buffer */
(paClipOff | paDitherOff),
patest1Callback,
&data );
if( err != paNoError ) goto error;
@ -167,7 +177,7 @@ PaError TestOnce( int buffersize )
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Waiting for sound to finish.\n");
Pa_Sleep(1000);
Pa_Sleep(1000*NUM_SECONDS);
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
@ -177,5 +187,6 @@ 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 ) );
fprintf( stderr, "Host Error message: %s\n", Pa_GetLastHostErrorInfo()->errorText );
return err;
}

View file

@ -0,0 +1,221 @@
/** @file patest_callbackstop.c
@brief Test the paComplete callback result code.
@author Ross Bencina <rossb@audiomulch.com>
*/
/*
* $Id$
*
* 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 (5)
#define NUM_LOOPS (4)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (67)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int phase;
unsigned long generatedFramesCount;
volatile int callbackReturnedPaComplete;
volatile int callbackInvokedAfterReturningPaComplete;
}
TestData;
/*
This routine will be called by the PortAudio stream 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 TestCallback( const void *input, void *output,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
TestData *data = (TestData*)userData;
float *out = (float*)output;
unsigned long i;
float x;
(void) input; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
if( data->callbackReturnedPaComplete )
data->callbackInvokedAfterReturningPaComplete = 1;
for( i=0; i<frameCount; i++ )
{
/* generate tone */
x = data->sine[ data->phase++ ];
if( data->phase >= TABLE_SIZE )
data->phase -= TABLE_SIZE;
*out++ = x; /* left */
*out++ = x; /* right */
}
data->generatedFramesCount += frameCount;
if( data->generatedFramesCount >= (NUM_SECONDS * SAMPLE_RATE) )
{
data->callbackReturnedPaComplete = 1;
return paComplete;
}
else
{
return paContinue;
}
}
/*----------------------------------------------------------------------------*/
int main(void);
int main(void)
{
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
TestData data;
int i, j;
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. );
}
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = Pa_GetDefaultOutputDevice();
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* output will be in-range, so no need to clip */
TestCallback,
&data );
if( err != paNoError ) goto error;
printf("Repeating test %d times.\n", NUM_LOOPS );
for( i=0; i < NUM_LOOPS; ++i )
{
data.phase = 0;
data.generatedFramesCount = 0;
data.callbackReturnedPaComplete = 0;
data.callbackInvokedAfterReturningPaComplete = 0;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Play for %d seconds.\n", NUM_SECONDS );
/* wait for the callback to complete generating NUM_SECONDS of tone */
do
{
Pa_Sleep( 500 );
}
while( !data.callbackReturnedPaComplete );
printf( "Callback returned paComplete.\n" );
printf( "Waiting for buffers to finish playing...\n" );
/* wait for stream to become inactive,
or for a timeout of approximately NUM_SECONDS
*/
j = 0;
while( (err = Pa_IsStreamActive( stream )) == 1 && j < NUM_SECONDS * 2 )
{
printf(".\n" );
Pa_Sleep( 500 );
++j;
}
if( err < 0 )
{
goto error;
}
else if( err == 1 )
{
printf( "TEST FAILED: Timed out waiting for buffers to finish playing.\n" );
}
else
{
printf("Buffers finished.\n" );
}
if( data.callbackInvokedAfterReturningPaComplete )
{
printf( "TEST FAILED: Callback was invoked after returning paComplete.\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

@ -1,10 +1,11 @@
/** @file patest_clip.c
@brief Play a sine wave for several seconds at an amplitude
that would require clipping.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -37,12 +38,14 @@
#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];
@ -51,21 +54,26 @@ typedef struct paTestData
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 )
static int sineCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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;
(void) timeInfo;
(void) statusFlags;
for( i=0; i<framesPerBuffer; i++ )
{
@ -83,33 +91,40 @@ int main(void);
int main(void)
{
PaError err;
paTestData DATA;
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. );
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 );
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 );
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 );
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 );
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 );
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" );
@ -120,35 +135,42 @@ error:
/*****************************************************************************/
PaError PlaySine( paTestData *data, unsigned long flags, float amplitude )
{
PortAudioStream *stream;
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
data->left_phase = data->right_phase = 0;
data->amplitude = amplitude;
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
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,
NULL, /* no input */
&outputParameters,
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 */
flags,
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 ) );
printf("CPULoad = %8.6f\n", Pa_GetStreamCpuLoad( stream ) );
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
return paNoError;
error:

View file

@ -1,10 +1,12 @@
/** @file patest_dither.c
@brief 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
*/
/*
* $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
@ -36,39 +38,40 @@
*/
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#define NUM_SECONDS (4)
#define NUM_SECONDS (5)
#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;
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,
static int sineCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, 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 */
@ -80,14 +83,69 @@ static int sineCallback( void *inputBuffer, void *outputBuffer,
}
return 0;
}
/*****************************************************************************/
/*
V18 version did not call Pa_Terminate() if Pa_Initialize() failed.
This V19 version ALWAYS calls Pa_Terminate(). PS.
*/
PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude );
PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude )
{
PaStream* stream;
PaStreamParameters outputParameters;
PaError err;
data->left_phase = data->right_phase = 0;
data->amplitude = amplitude;
err = Pa_Initialize();
if (err != paNoError)
goto done;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
/* When you change this, also */
/* adapt the callback routine! */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )
->defaultLowOutputLatency; /* Low latency. */
err = Pa_OpenStream( &stream,
NULL, /* No input. */
&outputParameters,
SAMPLE_RATE,
1024, /* frames per buffer */
flags,
sineCallback,
(void*)data );
if (err != paNoError)
goto done;
err = Pa_StartStream( stream );
if (err != paNoError)
goto done;
Pa_Sleep( NUM_SECONDS * 1000 );
printf("CPULoad = %8.6f\n", Pa_GetStreamCpuLoad(stream));
err = Pa_CloseStream( stream );
done:
Pa_Sleep( 250 ); /* Just a small silence. */
Pa_Terminate();
return err;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError err;
paTestData DATA;
int i;
float amplitude = 32.0 / (1<<15);
PaError err;
paTestData DATA;
int i;
float amplitude = 4.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++ )
@ -96,57 +154,25 @@ int main(void)
}
printf("\nNo treatment..\n"); fflush(stdout);
err = PlaySine( &DATA, paClipOff | paDitherOff, amplitude );
if( err < 0 ) goto error;
printf("\nClip.\n");
if( err < 0 ) goto done;
printf("\nClip..\n");
fflush(stdout);
err = PlaySine( &DATA, paDitherOff, amplitude );
if( err < 0 ) goto error;
printf("\nClip and Dither.\n");
if( err < 0 ) goto done;
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;
done:
if (err)
{
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 ) );
err = 1; /* Though PlaySine() already called Pa_Terminate(), */
} /* we may still call Pa_GetErrorText(). */
else
printf("\n(Don't forget to turn the VOLUME DOWN after listening so carefully.)\n");
return err; /* 0 or 1. */
}

View file

@ -1,10 +1,10 @@
/** @file patest_hang.c
@brief Play a sine then hang audio callback to test watchdog.
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $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/
@ -36,15 +36,15 @@
*/
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (1024)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (1024)
#ifndef M_PI
#define M_PI (3.14159265)
#define M_PI (3.14159265)
#endif
#define TWOPI (M_PI * 2.0)
#define TWOPI (M_PI * 2.0)
typedef struct paTestData
{
@ -57,9 +57,11 @@ paTestData;
** 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 )
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
@ -68,8 +70,7 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
double phaseInc = 0.02;
double phase = data->phase;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
(void) inputBuffer; /* Prevent unused argument warning. */
for( i=0; i<framesPerBuffer; i++ )
{
@ -92,40 +93,40 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
int i;
paTestData data = {0};
PaStream* stream;
PaStreamParameters outputParameters;
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;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */
outputParameters.channelCount = 1; /* Mono output. */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point. */
outputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)
->defaultLowOutputLatency;
err = Pa_OpenStream(&stream,
NULL, /* No input. */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* No out of range samples. */
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 )
/* Gradually increase sleep time. */
/* Was: for( i=0; i<10000; i+= 1000 ) */
for(i=0; i <= 1000; i += 100)
{
printf("Sleep for %d milliseconds in audio callback.\n", i );
data.sleepFor = i;

View file

@ -0,0 +1,224 @@
/** @file patest_in_overflow.c
@brief Count input overflows (using paInputOverflow flag) under
overloaded and normal conditions.
This test uses the same method to overload the stream as does
patest_out_underflow.c -- it generates sine waves until the cpu load
exceeds a certain level. However this test is only concerned with
input and so doesn't ouput any sound.
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $Id$
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2004 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 sineCount;
double phases[MAX_SINES];
int countOverflows;
int inputOverflowCount;
}
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( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float out; /* variable to hold dummy output */
unsigned long i;
int j;
int finished = paContinue;
(void) timeInfo; /* Prevent unused variable warning. */
(void) inputBuffer; /* Prevent unused variable warning. */
(void) outputBuffer; /* Prevent unused variable warning. */
if( data->countOverflows && (statusFlags & paInputOverflow) )
data->inputOverflowCount++;
for( i=0; i<framesPerBuffer; i++ )
{
float output = 0.0;
double phaseInc = 0.02;
double phase;
for( j=0; j<data->sineCount; 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;
}
/* this is an input-only stream so we don't actually use the output */
out = (float) (output / data->sineCount);
(void) out; /* suppress unused variable warning*/
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters inputParameters;
PaStream *stream;
PaError err;
int safeSineCount, stressedSineCount;
int safeOverflowCount, stressedOverflowCount;
paTestData data = {0};
double load;
printf("PortAudio Test: input only, no sound output. Load callback by performing calculations, count input overflows. SR = %d, BufSize = %d. MAX_LOAD = %f\n",
SAMPLE_RATE, FRAMES_PER_BUFFER, (float)MAX_LOAD );
err = Pa_Initialize();
if( err != paNoError ) goto error;
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
inputParameters.channelCount = 1; /* mono output */
inputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL, /* no output */
SAMPLE_RATE,
FRAMES_PER_BUFFER,
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("Establishing load conditions...\n" );
/* Determine number of sines required to get to 50% */
do
{
data.sineCount++;
Pa_Sleep( 100 );
load = Pa_GetStreamCpuLoad( stream );
printf("sineCount = %d, CPU load = %f\n", data.sineCount, load );
}
while( load < 0.5 && data.sineCount < (MAX_SINES-1));
safeSineCount = data.sineCount;
/* Calculate target stress value then ramp up to that level*/
stressedSineCount = (int) (2.0 * data.sineCount * MAX_LOAD );
if( stressedSineCount > MAX_SINES )
stressedSineCount = MAX_SINES;
for( ; data.sineCount < stressedSineCount; data.sineCount++ )
{
Pa_Sleep( 100 );
load = Pa_GetStreamCpuLoad( stream );
printf("STRESSING: sineCount = %d, CPU load = %f\n", data.sineCount, load );
}
printf("Counting overflows for 5 seconds.\n");
data.countOverflows = 1;
Pa_Sleep( 5000 );
stressedOverflowCount = data.inputOverflowCount;
data.countOverflows = 0;
data.sineCount = safeSineCount;
printf("Resuming safe load...\n");
Pa_Sleep( 1500 );
data.inputOverflowCount = 0;
Pa_Sleep( 1500 );
load = Pa_GetStreamCpuLoad( stream );
printf("sineCount = %d, CPU load = %f\n", data.sineCount, load );
printf("Counting overflows for 5 seconds.\n");
data.countOverflows = 1;
Pa_Sleep( 5000 );
safeOverflowCount = data.inputOverflowCount;
printf("Stop stream.\n");
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
if( stressedOverflowCount == 0 )
printf("Test failed, no input overflows detected under stress.\n");
else if( safeOverflowCount != 0 )
printf("Test failed, %d unexpected overflows detected under safe load.\n", safeOverflowCount);
else
printf("Test passed, %d expected input overflows detected under stress, 0 unexpected overflows detected under safe load.\n", stressedOverflowCount );
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

@ -1,9 +1,10 @@
/** @file
@brief 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
*/
/*
* $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
@ -33,27 +34,22 @@
* 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 (Pa_GetDefaultOutputDevice())
#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 */
@ -62,6 +58,7 @@ typedef struct
float right_phase;
}
paTestData;
float LookupSine( paTestData *data, float phase );
/* Convert phase between and 1.0 to sine value
* using linear interpolation.
@ -76,20 +73,22 @@ float LookupSine( paTestData *data, float phase )
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,
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
(void) inputBuffer; /* Prevent unused variable warning. */
for( i=0; i<framesPerBuffer; i++ )
{
@ -102,45 +101,52 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
}
return 0;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStream *stream;
PaStreamParameters outputParameters;
PaError err;
paTestData data;
int i;
int done = 0;
printf("PortAudio Test: enter letter then hit ENTER. numBuffers = %d\n", NUM_BUFFERS );
printf("PortAudio Test: enter letter then hit ENTER.\n" );
/* 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.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 );
outputParameters.device = OUTPUT_DEVICE;
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
printf("Requested output latency = %.4f seconds.\n", outputParameters.suggestedLatency );
printf("%d frames per buffer.\n.", FRAMES_PER_BUFFER );
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,
NULL, /* no input */
&outputParameters,
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 */
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");

View file

@ -1,12 +1,14 @@
/** @file patest_leftright.c
@brief Play different tone sine waves that
alternate between left and right channel.
The low tone should be on the left channel.
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $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/
@ -39,6 +41,7 @@
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#define NUM_SECONDS (8)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (512)
@ -59,15 +62,18 @@ paTestData;
** 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 )
static int patestCallback( const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
/* Prevent unused variable warnings. */
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
@ -103,7 +109,8 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStream *stream;
PaStreamParameters outputParameters;
PaError err;
paTestData data;
int i;
@ -122,23 +129,21 @@ int main(void)
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 );
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream( &stream,
NULL, /* No input. */
&outputParameters, /* As above. */
SAMPLE_RATE,
FRAMES_PER_BUFFER,
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 );
@ -148,7 +153,7 @@ int main(void)
timeout = NUM_SECONDS * 4;
while( timeout > 0 )
{
Pa_Sleep( 300 );
Pa_Sleep( 300 ); /*(Irix very much likes sleeps <= 1000 ms.)*/
timeout -= 1;
}

View file

@ -1,9 +1,9 @@
/** @file patest_longsine.c
@brief Play a sine wave until ENTER hit.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -33,8 +33,10 @@
* 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)
@ -56,15 +58,17 @@ paTestData;
** 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 )
static int patestCallback(const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData)
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned int i;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
(void) inputBuffer; /* Prevent unused argument warning. */
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
@ -81,7 +85,8 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
paTestData data;
int i;
@ -97,22 +102,20 @@ int main(void)
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 );
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream( &stream,
NULL, /* No input. */
&outputParameters, /* As above. */
SAMPLE_RATE,
256, /* Frames per buffer. */
paClipOff, /* No out of range samples expected. */
patestCallback,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );

View file

@ -1,9 +1,9 @@
/** @file patest_many.c
@brief Start and stop the PortAudio Driver multiple times.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -52,23 +52,27 @@ typedef struct
}
paTestData;
PaError TestOnce( void );
static int patest1Callback( void *inputBuffer, void *outputBuffer,
static int patest1Callback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData );
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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,
static int patest1Callback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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 )
{
@ -145,7 +149,8 @@ int main(int argc, char **argv)
#endif
PaError TestOnce( void )
{
PortAudioStream *stream;
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
paTestData data;
int i;
@ -159,20 +164,19 @@ PaError TestOnce( void )
data.sampsToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
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,
NULL, /* no input */
&outputParameters,
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 */
1024, /* frames per buffer */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patest1Callback,
&data );
if( err != paNoError ) goto error;

View file

@ -1,11 +1,10 @@
/** @file patest_maxsines.c
@brief How many sine waves can we calculate and play in less than 80% CPU Load.
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $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/
@ -82,9 +81,12 @@ float LookupSine( paTestData *data, float phase )
** 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 )
static int patestCallback(const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
@ -94,10 +96,9 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned long i;
int j;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
(void) inputBuffer; /* Prevent unused argument warning. */
/* Detemine amplitude scaling factor */
/* Determine amplitude scaling factor */
numForScale = data->numSines;
if( numForScale < 8 ) numForScale = 8; /* prevent pops at beginning */
scaler = 1.0f / numForScale;
@ -132,11 +133,13 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
int main(void);
int main(void)
{
int i;
PortAudioStream *stream;
PaError err;
paTestData data = {0};
double load;
int i;
PaStream* stream;
PaStreamParameters outputParameters;
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 */
@ -147,43 +150,48 @@ int main(void)
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;
if( err != paNoError )
goto error;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */
outputParameters.channelCount = 2; /* Stereo output. */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
outputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)
->defaultHighOutputLatency;
err = Pa_OpenStream(&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* No out of range samples should occur. */
patestCallback,
&data);
if( err != paNoError )
goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError )
goto error;
/* Play an increasing number of sine waves until we hit MAX_USAGE */
do
{
/* Play an increasing number of sine waves until we hit MAX_USAGE */
do {
data.numSines++;
Pa_Sleep( 200 );
load = Pa_GetCPULoad( stream );
Pa_Sleep(200);
load = Pa_GetStreamCpuLoad(stream);
printf("numSines = %d, CPU load = %f\n", data.numSines, load );
fflush( stdout );
}
while( (load < MAX_USAGE) && (data.numSines < MAX_SINES) );
fflush(stdout);
} while((load < MAX_USAGE) && (data.numSines < MAX_SINES));
Pa_Sleep(2000); /* Stay for 2 seconds around 80% CPU. */
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError )
goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError )
goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;

145
pa_tests/patest_mono.c Normal file
View file

@ -0,0 +1,145 @@
/*
* $Id$
* patest_sine.c
* Play a monophonic 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 (10)
#define SAMPLE_RATE (44100)
#define AMPLITUDE (0.8)
#define FRAMES_PER_BUFFER (64)
#define OUTPUT_DEVICE Pa_GetDefaultOutputDevice()
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int 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( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
/* avoid unused variable warnings */
(void) inputBuffer;
(void) timeInfo;
(void) statusFlags;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->phase]; /* left */
data->phase += 1;
if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE;
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
paTestData data;
int i;
printf("PortAudio Test: output MONO 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) (AMPLITUDE * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
}
data.phase = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = OUTPUT_DEVICE;
outputParameters.channelCount = 1; /* MONO output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
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 ); fflush(stdout);
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,155 @@
/*
* $Id$
* patest_sine.c
* Play a monophonic 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"
#include "pa_asio.h"
#define NUM_SECONDS (10)
#define SAMPLE_RATE (44100)
#define AMPLITUDE (0.8)
#define FRAMES_PER_BUFFER (64)
#define OUTPUT_DEVICE Pa_GetDefaultOutputDevice()
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int 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( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
/* avoid unused variable warnings */
(void) inputBuffer;
(void) timeInfo;
(void) statusFlags;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->phase]; /* left */
data->phase += 1;
if( data->phase >= TABLE_SIZE ) data->phase -= TABLE_SIZE;
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters outputParameters;
PaAsioStreamInfo asioOutputInfo;
PaStream *stream;
PaError err;
paTestData data;
int outputChannelSelectors[1];
int i;
printf("PortAudio Test: output MONO 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) (AMPLITUDE * sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ));
}
data.phase = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = OUTPUT_DEVICE;
outputParameters.channelCount = 1; /* MONO output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
asioOutputInfo.size = sizeof(PaAsioStreamInfo);
asioOutputInfo.hostApiType = paASIO;
asioOutputInfo.version = 1;
asioOutputInfo.flags = paAsioUseChannelSelectors;
outputChannelSelectors[0] = 1; /* select the second (right) ASIO device channel */
asioOutputInfo.channelSelectors = outputChannelSelectors;
outputParameters.hostApiSpecificStreamInfo = &asioOutputInfo;
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
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 ); fflush(stdout);
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

@ -1,10 +1,9 @@
/** @file patest_multi_sine.c
@brief Play a different sine wave on each channel.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -34,15 +33,16 @@
* 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)
#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)
@ -50,8 +50,9 @@
typedef struct
{
int numChannels;
double phases[MAX_CHANNELS];
short interleaved; /* Nonzero for interleaved / zero for non-interleaved. */
int numChannels; /* Actually used. */
double phases[MAX_CHANNELS]; /* Each channel gets its' own frequency. */
}
paTestData;
@ -59,86 +60,135 @@ paTestData;
** 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 )
static int patestCallback(const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData)
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int frameIndex, channelIndex;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) inputBuffer;
int frameIndex, channelIndex;
float** outputs = (float**)outputBuffer;
paTestData* data = (paTestData*)userData;
for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
{
for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
(void) inputBuffer; /* Prevent unused arg warning. */
if (data->interleaved)
{
/* Output sine wave on every channel. */
*out++ = (float) sin(data->phases[channelIndex]);
float *out = (float*)outputBuffer; /* interleaved version */
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);
/* 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);
}
}
}
}
else
{
for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ )
{
for( channelIndex=0; channelIndex<data->numChannels; channelIndex++ )
{
/* Output sine wave on every channel. */
outputs[channelIndex][frameIndex] = (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 test(short interleaved)
{
PaStream* stream;
PaStreamParameters outputParameters;
PaError err;
const PaDeviceInfo* pdi;
paTestData data;
short n;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device, max channels. */
pdi = Pa_GetDeviceInfo(outputParameters.device);
outputParameters.channelCount = pdi->maxOutputChannels;
if (outputParameters.channelCount > MAX_CHANNELS)
outputParameters.channelCount = MAX_CHANNELS;
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = pdi->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
data.interleaved = interleaved;
data.numChannels = outputParameters.channelCount;
for (n = 0; n < data.numChannels; n++)
data.phases[n] = 0.0; /* Phases wrap and maybe don't need initialisation. */
printf("%d ", data.numChannels);
if (interleaved)
printf("interleaved ");
else
{
printf(" non-interleaved ");
outputParameters.sampleFormat |= paNonInterleaved;
}
printf("channels.\n");
err = Pa_OpenStream(&stream,
NULL, /* No input. */
&outputParameters,
SAMPLE_RATE, /* Sample rate. */
FRAMES_PER_BUFFER, /* Frames per buffer. */
paClipOff, /* Samples never out of range, no clipping. */
patestCallback,
&data);
if (err == paNoError)
{
err = Pa_StartStream(stream);
if (err == paNoError)
{
printf("Hit ENTER to stop this test.\n");
getchar();
err = Pa_StopStream(stream);
}
Pa_CloseStream( stream );
}
return err;
}
/*******************************************************************/
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;
if (err != paNoError)
goto done;
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 = test(1); /* 1 means interleaved. */
if (err != paNoError)
goto done;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
err = test(0); /* 0 means not interleaved. */
if (err != paNoError)
goto done;
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:
done:
if (err)
{
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));
}
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;
return 0;
}

View file

@ -0,0 +1,217 @@
/** @file patest_out_underflow.c
@brief Count output underflows (using paOutputUnderflow flag)
under overloaded and normal conditions.
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $Id$
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.audiomulch.com/portaudio/
* Copyright (c) 1999-2004 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 sineCount;
double phases[MAX_SINES];
int countUnderflows;
int outputUnderflowCount;
}
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( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int j;
int finished = paContinue;
(void) timeInfo; /* Prevent unused variable warning. */
(void) inputBuffer; /* Prevent unused variable warning. */
if( data->countUnderflows && (statusFlags & paOutputUnderflow) )
data->outputUnderflowCount++;
for( i=0; i<framesPerBuffer; i++ )
{
float output = 0.0;
double phaseInc = 0.02;
double phase;
for( j=0; j<data->sineCount; 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->sineCount);
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
int safeSineCount, stressedSineCount;
int safeUnderflowCount, stressedUnderflowCount;
paTestData data = {0};
double load;
printf("PortAudio Test: output sine waves, count underflows. SR = %d, BufSize = %d. MAX_LOAD = %f\n",
SAMPLE_RATE, FRAMES_PER_BUFFER, (float)MAX_LOAD );
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 1; /* mono output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
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("Establishing load conditions...\n" );
/* Determine number of sines required to get to 50% */
do
{
data.sineCount++;
Pa_Sleep( 100 );
load = Pa_GetStreamCpuLoad( stream );
printf("sineCount = %d, CPU load = %f\n", data.sineCount, load );
}
while( load < 0.5 && data.sineCount < (MAX_SINES-1));
safeSineCount = data.sineCount;
/* Calculate target stress value then ramp up to that level*/
stressedSineCount = (int) (2.0 * data.sineCount * MAX_LOAD );
if( stressedSineCount > MAX_SINES )
stressedSineCount = MAX_SINES;
for( ; data.sineCount < stressedSineCount; data.sineCount++ )
{
Pa_Sleep( 100 );
load = Pa_GetStreamCpuLoad( stream );
printf("STRESSING: sineCount = %d, CPU load = %f\n", data.sineCount, load );
}
printf("Counting underflows for 5 seconds.\n");
data.countUnderflows = 1;
Pa_Sleep( 5000 );
stressedUnderflowCount = data.outputUnderflowCount;
data.countUnderflows = 0;
data.sineCount = safeSineCount;
printf("Resuming safe load...\n");
Pa_Sleep( 1500 );
data.outputUnderflowCount = 0;
Pa_Sleep( 1500 );
load = Pa_GetStreamCpuLoad( stream );
printf("sineCount = %d, CPU load = %f\n", data.sineCount, load );
printf("Counting underflows for 5 seconds.\n");
data.countUnderflows = 1;
Pa_Sleep( 5000 );
safeUnderflowCount = data.outputUnderflowCount;
printf("Stop stream.\n");
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Terminate();
if( stressedUnderflowCount == 0 )
printf("Test failed, no output underflows detected under stress.\n");
else if( safeUnderflowCount != 0 )
printf("Test failed, %d unexpected underflows detected under safe load.\n", safeUnderflowCount);
else
printf("Test passed, %d expected output underflows detected under stress, 0 unexpected underflows detected under safe load.\n", stressedUnderflowCount );
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

@ -1,18 +1,22 @@
/** @file patest_pink.c
@brief Generate Pink Noise using Gardner method.
Optimization suggested by James McCartney uses a tree
to select which random value to replace.
<pre>
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
</pre>
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
*/
/*
* $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
@ -42,12 +46,15 @@
* 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];
@ -57,10 +64,12 @@ typedef struct
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 )
@ -70,6 +79,7 @@ static unsigned long GenerateRandomNumber( void )
randSeed = (randSeed * 196314165) + 907633515;
return randSeed;
}
/************************************************************/
/* Setup PinkNoise structure for N rows of generators. */
void InitializePinkNoise( PinkNoise *pink, int numRows )
@ -85,11 +95,13 @@ void InitializePinkNoise( PinkNoise *pink, int numRows )
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 )
{
@ -132,9 +144,11 @@ float GeneratePinkNoise( PinkNoise *pink )
#endif
return output;
}
/*******************************************************************/
#define PINK_TEST
#ifdef PINK_TEST
/* Context for callback routine. */
typedef struct
{
@ -143,13 +157,17 @@ typedef struct
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 )
static int patestCallback(const void* inputBuffer,
void* outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void* userData)
{
int finished;
int i;
@ -157,7 +175,6 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
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 )
@ -178,17 +195,23 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
data->sampsToGo -= numFrames;
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int totalSamps;
PaStream* stream;
PaError err;
paTestData data;
PaStreamParameters outputParameters;
int totalSamps;
static const double SR = 44100.0;
static const int FPB = 2048; /* Frames per buffer: 46 ms buffers. */
/* Initialize two pink noise signals with different numbers of rows. */
InitializePinkNoise( &data.leftPink, 12 );
InitializePinkNoise( &data.leftPink, 12 );
InitializePinkNoise( &data.rightPink, 16 );
/* Look at a few values. */
{
int i;
@ -199,35 +222,36 @@ int main(void)
printf("Pink = %f\n", pink );
}
}
data.sampsToGo = totalSamps = 8*44100; /* Play for a few seconds. */
data.sampsToGo = totalSamps = (int)(60.0 * SR); /* Play a whole minute. */
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 );
outputParameters.device = Pa_GetDefaultOutputDevice(); /* Take the default output device. */
outputParameters.channelCount = 2; /* Stereo output, most likely supported. */
outputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output. */
outputParameters.suggestedLatency =
Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
err = Pa_OpenStream(&stream,
NULL, /* No input. */
&outputParameters,
SR, /* Sample rate. */
FPB, /* Frames per buffer. */
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! */
}
printf("Stereo pink noise for one minute...\n");
while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
if( err < 0 ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
#ifdef PINK_MEASURE

223
pa_tests/patest_prime.c Normal file
View file

@ -0,0 +1,223 @@
/** @file patest_prime.c
@brief Test stream priming mode.
@author Ross Bencina http://www.audiomulch.com/~rossb
*/
/*
* $Id$
*
* 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_util.h"
#define NUM_BEEPS (3)
#define SAMPLE_RATE (44100)
#define SAMPLE_PERIOD (1.0/44100.0)
#define FRAMES_PER_BUFFER (256)
#define BEEP_DURATION (400)
#define IDLE_DURATION (SAMPLE_RATE*2) /* 2 seconds */
#define SLEEP_MSEC (50)
#define STATE_BKG_IDLE (0)
#define STATE_BKG_BEEPING (1)
typedef struct
{
float leftPhase;
float rightPhase;
int state;
int beepCountdown;
int idleCountdown;
}
paTestData;
static void InitializeTestData( paTestData *testData )
{
testData->leftPhase = 0;
testData->rightPhase = 0;
testData->state = STATE_BKG_BEEPING;
testData->beepCountdown = BEEP_DURATION;
testData->idleCountdown = IDLE_DURATION;
}
/* 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( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData )
{
/* Cast data passed through stream to our structure. */
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned int i;
int result = paContinue;
/* supress unused parameter warnings */
(void) inputBuffer;
(void) timeInfo;
(void) statusFlags;
for( i=0; i<framesPerBuffer; i++ )
{
switch( data->state )
{
case STATE_BKG_IDLE:
*out++ = 0.0; /* left */
*out++ = 0.0; /* right */
--data->idleCountdown;
if( data->idleCountdown <= 0 ) result = paComplete;
break;
case STATE_BKG_BEEPING:
if( data->beepCountdown <= 0 )
{
data->state = STATE_BKG_IDLE;
*out++ = 0.0; /* left */
*out++ = 0.0; /* right */
}
else
{
/* Play sawtooth wave. */
*out++ = data->leftPhase; /* left */
*out++ = data->rightPhase; /* right */
/* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
data->leftPhase += 0.01f;
/* When signal reaches top, drop back down. */
if( data->leftPhase >= 1.0f ) data->leftPhase -= 2.0f;
/* higher pitch so we can distinguish left and right. */
data->rightPhase += 0.03f;
if( data->rightPhase >= 1.0f ) data->rightPhase -= 2.0f;
}
--data->beepCountdown;
break;
}
}
return result;
}
/*******************************************************************/
static PaError DoTest( int flags )
{
PaStream *stream;
PaError err;
paTestData data;
PaStreamParameters outputParameters;
InitializeTestData( &data );
outputParameters.device = Pa_GetDefaultOutputDevice();
outputParameters.channelCount = 2;
outputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.sampleFormat = paFloat32;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
/* Open an audio I/O stream. */
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
paClipOff | flags, /* 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("hear \"BEEP\"\n" );
fflush(stdout);
while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(SLEEP_MSEC);
if( err < 0 ) goto error;
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
return err;
error:
return err;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaError err = paNoError;
int i;
/* Initialize library before making any other calls. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
printf("PortAudio Test: Testing stream playback with no priming.\n");
printf("PortAudio Test: you should see BEEP before you hear it.\n");
printf("BEEP %d times.\n", NUM_BEEPS );
for( i=0; i< NUM_BEEPS; ++i )
{
err = DoTest( 0 );
if( err != paNoError )
goto error;
}
printf("PortAudio Test: Testing stream playback with priming.\n");
printf("PortAudio Test: you should see BEEP around the same time you hear it.\n");
for( i=0; i< NUM_BEEPS; ++i )
{
err = DoTest( paPrimeOutputBuffersUsingStreamCallback );
if( err != paNoError )
goto error;
}
printf("Test finished.\n");
Pa_Terminate();
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,229 @@
/** @file patest_read_record.c
@brief Record input into an array; Save array to a file; Playback recorded
data. Implemented using the blocking API (Pa_ReadStream(), Pa_WriteStream() )
@author Phil Burk http://www.softsynth.com
@author Ross Bencina rossb@audiomulch.com
*/
/*
* $Id$
*
* 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 FRAMES_PER_BUFFER (1024)
#define NUM_SECONDS (5)
#define NUM_CHANNELS (2)
/* #define DITHER_FLAG (paDitherOff) */
#define DITHER_FLAG (0) /**/
/* Select sample format. */
#if 1
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#define SAMPLE_SILENCE (0.0f)
#define PRINTF_S_FORMAT "%.8f"
#elif 1
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#elif 0
#define PA_SAMPLE_TYPE paInt8
typedef char SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#else
#define PA_SAMPLE_TYPE paUInt8
typedef unsigned char SAMPLE;
#define SAMPLE_SILENCE (128)
#define PRINTF_S_FORMAT "%d"
#endif
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters inputParameters, outputParameters;
PaStream *stream;
PaError err;
SAMPLE *recordedSamples;
int i;
int totalFrames;
int numSamples;
int numBytes;
SAMPLE max, average, val;
printf("patest_read_record.c\n"); fflush(stdout);
totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */
numSamples = totalFrames * NUM_CHANNELS;
numBytes = numSamples * sizeof(SAMPLE);
recordedSamples = (SAMPLE *) malloc( numBytes );
if( recordedSamples == NULL )
{
printf("Could not allocate record array.\n");
exit(1);
}
for( i=0; i<numSamples; i++ ) recordedSamples[i] = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
inputParameters.channelCount = NUM_CHANNELS;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
/* Record some audio. -------------------------------------------- */
err = Pa_OpenStream(
&stream,
&inputParameters,
NULL, /* &outputParameters, */
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
NULL, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Now recording!!\n"); fflush(stdout);
err = Pa_ReadStream( stream, recordedSamples, totalFrames );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
/* Measure maximum peak amplitude. */
max = 0;
average = 0;
for( i=0; i<numSamples; i++ )
{
val = recordedSamples[i];
if( val < 0 ) val = -val; /* ABS */
if( val > max )
{
max = val;
}
average += val;
}
average = average / numSamples;
printf("Sample max amplitude = "PRINTF_S_FORMAT"\n", max );
printf("Sample average = "PRINTF_S_FORMAT"\n", average );
/* Was as below. Better choose at compile time because this
keeps generating compiler-warnings:
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( recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid );
fclose( fid );
printf("Wrote data to 'recorded.raw'\n");
}
}
#endif
/* Playback recorded data. -------------------------------------------- */
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = NUM_CHANNELS;
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
printf("Begin playback.\n"); fflush(stdout);
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
NULL, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
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);
err = Pa_WriteStream( stream, recordedSamples, totalFrames );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
printf("Done.\n"); fflush(stdout);
}
free( 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,178 @@
/** @file patest_read_write_wire.c
@brief Tests ifull duplex blocking I/O by passing input straight to output.
@author Bjorn Roche. XO Audio LLC for Z-Systems Engineering.
@author based on code by: Phil Burk http://www.softsynth.com
@author based on code by: Ross Bencina rossb@audiomulch.com
*/
/*
* $Id: patest_read_record.c 757 2004-02-13 07:48:10Z rossbencina $
*
* 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 FRAMES_PER_BUFFER (1024)
#define NUM_CHANNELS (2)
/* #define DITHER_FLAG (paDitherOff) */
#define DITHER_FLAG (0) /**/
/* Select sample format. */
#if 1
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#define SAMPLE_SILENCE (0.0f)
#define PRINTF_S_FORMAT "%.8f"
#elif 1
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#elif 0
#define PA_SAMPLE_TYPE paInt8
typedef char SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#else
#define PA_SAMPLE_TYPE paUInt8
typedef unsigned char SAMPLE;
#define SAMPLE_SILENCE (128)
#define PRINTF_S_FORMAT "%d"
#endif
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters inputParameters, outputParameters;
PaStream *stream;
PaError err;
SAMPLE *sampleBlock;
int i;
int numBytes;
printf("patest_read_write_wire.c\n"); fflush(stdout);
numBytes = FRAMES_PER_BUFFER * NUM_CHANNELS * sizeof(SAMPLE);
sampleBlock = (SAMPLE *) malloc( numBytes );
if( sampleBlock == NULL )
{
printf("Could not allocate record array.\n");
exit(1);
}
for( i=0; i<FRAMES_PER_BUFFER*NUM_CHANNELS; i++ )
sampleBlock[i] = (SAMPLE_SILENCE);
err = Pa_Initialize();
if( err != paNoError ) goto error;
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
printf( "Input LL: %g s\n", Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency );
printf( "Input HL: %g s\n", Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency );
inputParameters.channelCount = NUM_CHANNELS;
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultHighInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
printf( "Output LL: %g s\n", Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency );
printf( "Output HL: %g s\n", Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency );
outputParameters.channelCount = NUM_CHANNELS;
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
/* -- setup -- */
err = Pa_OpenStream(
&stream,
&inputParameters,
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
NULL, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Wire on. Will run one minute.\n"); fflush(stdout);
for( i=0; i<(60*SAMPLE_RATE)/FRAMES_PER_BUFFER; ++i )
{
err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) goto xrun;
err = Pa_ReadStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) goto xrun;
}
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("Wire on. Interrupt to stop.\n"); fflush(stdout);
while( 1 )
{
err = Pa_WriteStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) goto xrun;
err = Pa_ReadStream( stream, sampleBlock, FRAMES_PER_BUFFER );
if( err ) goto xrun;
}
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
free( sampleBlock );
Pa_Terminate();
return 0;
xrun:
Pa_Terminate();
if( err & paInputOverflow )
fprintf( stderr, "Input Overflow.\n" );
if( err & paOutputUnderflow )
fprintf( stderr, "Output Underflow.\n" );
return -2;
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

@ -1,11 +1,9 @@
/** @file patest_record.c
@brief Record input into an array; Save array to a file; Playback recorded data.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -35,35 +33,40 @@
* 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 (17932) // Test failure to open with this value. */
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (1024)
#define NUM_SECONDS (5)
#define NUM_CHANNELS (2)
/* #define DITHER_FLAG (paDitherOff) /**/
/* #define DITHER_FLAG (paDitherOff) */
#define DITHER_FLAG (0) /**/
/* Select sample format. */
#if 0
#if 1
#define PA_SAMPLE_TYPE paFloat32
typedef float SAMPLE;
#define SAMPLE_SILENCE (0.0f)
#elif 0
#define PRINTF_S_FORMAT "%.8f"
#elif 1
#define PA_SAMPLE_TYPE paInt16
typedef short SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#elif 0
#define PA_SAMPLE_TYPE paInt8
typedef char SAMPLE;
#define SAMPLE_SILENCE (0)
#define PRINTF_S_FORMAT "%d"
#else
#define PA_SAMPLE_TYPE paUInt8
typedef unsigned char SAMPLE;
#define SAMPLE_SILENCE (128)
#define PRINTF_S_FORMAT "%d"
#endif
typedef struct
@ -73,16 +76,19 @@ typedef struct
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,
static int recordCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = (SAMPLE*)inputBuffer;
const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
long framesToCalc;
long i;
@ -90,18 +96,21 @@ static int recordCallback( void *inputBuffer, void *outputBuffer,
unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
(void) outputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
(void) timeInfo;
(void) statusFlags;
(void) userData;
if( framesLeft < framesPerBuffer )
{
framesToCalc = framesLeft;
finished = 1;
finished = paComplete;
}
else
{
framesToCalc = framesPerBuffer;
finished = 0;
finished = paContinue;
}
if( inputBuffer == NULL )
{
for( i=0; i<framesToCalc; i++ )
@ -126,9 +135,11 @@ static int recordCallback( void *inputBuffer, void *outputBuffer,
** 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,
static int playCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
@ -136,8 +147,11 @@ static int playCallback( void *inputBuffer, void *outputBuffer,
unsigned int i;
int finished;
unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) outTime;
(void) timeInfo;
(void) statusFlags;
(void) userData;
if( framesLeft < framesPerBuffer )
{
@ -153,7 +167,7 @@ static int playCallback( void *inputBuffer, void *outputBuffer,
if( NUM_CHANNELS == 2 ) *wptr++ = 0; /* right */
}
data->frameIndex += framesLeft;
finished = 1;
finished = paComplete;
}
else
{
@ -163,7 +177,7 @@ static int playCallback( void *inputBuffer, void *outputBuffer,
if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++; /* right */
}
data->frameIndex += framesPerBuffer;
finished = 0;
finished = paContinue;
}
return finished;
}
@ -172,67 +186,70 @@ static int playCallback( void *inputBuffer, void *outputBuffer,
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int totalFrames;
int numSamples;
int numBytes;
SAMPLE max, average, val;
PaStreamParameters inputParameters,
outputParameters;
PaStream* stream;
PaError err = paNoError;
paTestData data;
int i;
int totalFrames;
int numSamples;
int numBytes;
SAMPLE max, val;
double average;
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 );
data.recordedSamples = (SAMPLE *) malloc( numBytes ); /* From now on, recordedSamples is initialised. */
if( data.recordedSamples == NULL )
{
printf("Could not allocate record array.\n");
exit(1);
goto done;
}
for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
if( err != paNoError ) goto done;
inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
inputParameters.channelCount = 2; /* stereo input */
inputParameters.sampleFormat = PA_SAMPLE_TYPE;
inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
/* Record some audio. -------------------------------------------- */
err = Pa_OpenStream(
&stream,
Pa_GetDefaultInputDeviceID(),
NUM_CHANNELS, /* stereo input */
PA_SAMPLE_TYPE,
NULL,
paNoDevice,
0,
PA_SAMPLE_TYPE,
NULL,
&inputParameters,
NULL, /* &outputParameters, */
SAMPLE_RATE,
1024, /* frames per buffer */
0, /* number of buffers, if zero then use default minimum */
0, //paDitherOff, /* flags */
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
recordCallback,
&data );
if( err != paNoError ) goto error;
if( err != paNoError ) goto done;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError ) goto done;
printf("Now recording!!\n"); fflush(stdout);
while( Pa_StreamActive( stream ) )
while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
{
Pa_Sleep(1000);
printf("index = %d\n", data.frameIndex ); fflush(stdout);
}
if( err < 0 ) goto done;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError ) goto done;
/* Measure maximum peak amplitude. */
max = 0;
average = 0;
average = 0.0;
for( i=0; i<numSamples; i++ )
{
val = data.recordedSamples[i];
@ -243,20 +260,12 @@ int main(void)
}
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 );
}
average = average / (double)numSamples;
printf("sample max amplitude = "PRINTF_S_FORMAT"\n", max );
printf("sample average = %lf\n", average );
/* Write recorded data to a file. */
#if 0
{
@ -277,46 +286,52 @@ int main(void)
/* Playback recorded data. -------------------------------------------- */
data.frameIndex = 0;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = PA_SAMPLE_TYPE;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
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,
NULL, /* no input */
&outputParameters,
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 */
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
playCallback,
&data );
if( err != paNoError ) goto error;
if( err != paNoError ) goto done;
if( stream )
{
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError ) goto done;
printf("Waiting for playback to finish.\n"); fflush(stdout);
while( Pa_StreamActive( stream ) ) Pa_Sleep(100);
while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
if( err < 0 ) goto done;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError ) goto done;
printf("Done.\n"); fflush(stdout);
}
free( data.recordedSamples );
done:
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;
if( data.recordedSamples ) /* Sure it is NULL or valid. */
free( data.recordedSamples );
if( err != paNoError )
{
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 ) );
err = 1; /* Always return 0 or 1, but no other return codes. */
}
return err;
}

View file

@ -1,16 +1,53 @@
/* $Id$ */
/** @file patest_ringmix.c
@brief Ring modulate inputs to left output, mix inputs to right output.
*/
/*
* $Id$
*
* 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 "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 )
static int myCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
float *out = (float *) outputBuffer;
float *in = (float *) inputBuffer;
const float *in = (const float *) inputBuffer;
float *out = (float *) outputBuffer;
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++ )
{
@ -21,16 +58,17 @@ static int myCallback( void *inputBuffer, void *outputBuffer,
}
return 0;
}
/* Open a PortAudioStream to input and output audio data. */
int main(void)
{
PortAudioStream *stream;
PaStream *stream;
Pa_Initialize();
Pa_OpenDefaultStream(
&stream,
2, 2, /* stereo input and output */
2, 2, /* stereo input and output */
paFloat32, 44100.0,
64, 0, /* 64 frames per buffer, let PA determine numBuffers */
64, /* 64 frames per buffer */
myCallback, NULL );
Pa_StartStream( stream );
Pa_Sleep( 10000 ); /* Sleep for 10 seconds while processing. */

View file

@ -1,9 +1,9 @@
/** @file patest_saw.c
@brief Play a simple (aliasing) sawtooth wave.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -38,26 +38,29 @@
#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,
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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;
(void) inputBuffer; /* Prevent unused variable warning. */
for( i=0; i<framesPerBuffer; i++ )
{
@ -73,35 +76,39 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
}
return 0;
}
/*******************************************************************/
static paTestData data;
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStream *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 );
err = Pa_OpenDefaultStream( &stream,
0, /* no input channels */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
SAMPLE_RATE,
256, /* frames per buffer */
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 );

View file

@ -1,14 +1,13 @@
/** @file patest_sine.c
@brief Play a sine wave for several seconds.
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $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/
* 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
@ -60,16 +59,20 @@ paTestData;
** 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 )
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(void) inputBuffer;
for( i=0; i<framesPerBuffer; i++ )
{
*out++ = data->sine[data->left_phase]; /* left */
@ -79,54 +82,65 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
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;
return paContinue;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStreamParameters outputParameters;
PaStream *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;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
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,
NULL, /* no input */
&outputParameters,
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();

View file

@ -1,10 +1,9 @@
/** @file patest_sine8.c
@brief Test 8 bit data: play a sine wave for several seconds.
@author Ross Bencina <rossb@audiomulch.com>
*/
/*
* $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/
@ -34,21 +33,26 @@
* 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)
#define TABLE_SIZE (200)
#define TEST_UNSIGNED (0)
#if TEST_UNSIGNED
#define TEST_FORMAT paUInt8
#define TEST_FORMAT paUInt8
#else
#define TEST_FORMAT paInt8
#define TEST_FORMAT paInt8
#endif
#ifndef M_PI
#define M_PI (3.14159265)
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (200)
typedef struct
{
#if TEST_UNSIGNED
@ -61,21 +65,23 @@ 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,
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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;
(void) inputBuffer; /* Prevent unused variable warnings. */
if( data->framesToGo < framesPerBuffer )
{
@ -112,15 +118,18 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
}
return finished;
}
/*******************************************************************/
int main(void);
int main(void)
{
PortAudioStream *stream;
PaError err;
paTestData data;
int i;
int totalSamps;
PaStreamParameters outputParameters;
PaStream* stream;
PaError err;
paTestData data;
PaTime streamOpened;
int i, totalSamps;
#if TEST_UNSIGNED
printf("PortAudio Test: output UNsigned 8 bit sine wave.\n");
#else
@ -133,45 +142,64 @@ int main(void)
#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;
if( err != paNoError )
goto error;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */
outputParameters.channelCount = 2; /* Stereo output. */
outputParameters.sampleFormat = TEST_FORMAT;
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream( &stream,
NULL, /* No input. */
&outputParameters,
SAMPLE_RATE,
256, /* Frames per buffer. */
paClipOff, /* We won't output out of range samples so don't bother clipping them. */
patestCallback,
&data );
if( err != paNoError )
goto error;
streamOpened = Pa_GetStreamTime( stream ); /* Time in seconds when stream was opened (approx). */
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
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. */
/* (Was ( Pa_StreamTime( stream ) < (totalSamps/2) ) in V18. */
while( (Pa_GetStreamTime( stream ) - streamOpened) < (PaTime)NUM_SECONDS / 2.0 )
Pa_Sleep(10);
/* Stop sound until ENTER hit. (Hu? don't see any keyboard-input here.) */
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError )
goto error;
printf("Pause for 2 seconds.\n");
Pa_Sleep( 2000 );
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError )
goto error;
printf("Waiting for sound to finish.\n");
while( Pa_StreamActive( stream ) ) Pa_Sleep(10);
while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
Pa_Sleep(100);
if( err < 0 )
goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
if( err != paNoError )
goto error;
Pa_Terminate();
printf("Test finished.\n");
return err;

View file

@ -1,10 +1,9 @@
/** @file patest_sine_formats.c
@brief Play a sine wave for several seconds. Test various data formats.
@author Phil Burk
*/
/*
* $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/
@ -38,7 +37,7 @@
#include <math.h>
#include "portaudio.h"
#define NUM_SECONDS (20)
#define NUM_SECONDS (10)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (512)
#define LEFT_FREQ (SAMPLE_RATE/256.0) /* So we hit 1.0 */
@ -96,17 +95,19 @@ paTestData;
** 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,
static int patestCallback( const void *inputBuffer,
void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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;
(void) inputBuffer; /* Prevent unused variable warnings. */
if( data->framesToGo < framesPerBuffer )
{
@ -142,42 +143,42 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStream *stream;
PaStreamParameters outputParameters;
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 );
outputParameters.device = Pa_GetDefaultOutputDevice(); /* Default output device. */
outputParameters.channelCount = 2; /* Stereo output */
outputParameters.sampleFormat = TEST_FORMAT; /* Selected above. */
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream( &stream,
NULL, /* No input. */
&outputParameters, /* As above. */
SAMPLE_RATE,
FRAMES_PER_BUFFER,
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);
while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
if( err < 0 ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;

View file

@ -1,13 +1,11 @@
/** @file patest_sine_time.c
@brief Play a sine wave for several seconds, pausing in the middle.
Uses the Pa_GetStreamTime() call.
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $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/
@ -39,78 +37,78 @@
*/
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#include "pa_util.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 TWOPI (M_PI * 2.0)
#define TABLE_SIZE (200)
typedef struct
{
float sine[TABLE_SIZE];
int left_phase;
int right_phase;
int framesToGo;
volatile PaTimestamp outTime;
double left_phase;
double right_phase;
volatile PaTime 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 )
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
int i;
int framesToCalc;
int finished = 0;
(void) outTime; /* Prevent unused variable warnings. */
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;
(void) statusFlags; /* Prevent unused variable warnings. */
(void) inputBuffer;
data->outTime = outTime;
if( data->framesToGo < framesPerBuffer )
data->outTime = timeInfo->outputBufferDacTime;
for( i=0; i<framesPerBuffer; i++ )
{
framesToCalc = data->framesToGo;
data->framesToGo = 0;
finished = 1;
}
else
{
framesToCalc = framesPerBuffer;
data->framesToGo -= framesPerBuffer;
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 );
}
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;
data->left_phase = left_phase;
data->right_phase = right_phase;
return paContinue;
}
/*******************************************************************/
static void ReportStreamTime( PortAudioStream *stream, paTestData *data );
static void ReportStreamTime( PortAudioStream *stream, paTestData *data )
static void ReportStreamTime( PaStream *stream, paTestData *data );
static void ReportStreamTime( PaStream *stream, paTestData *data )
{
PaTimestamp streamTime, latency, outTime;
PaTime streamTime, latency, outTime;
streamTime = Pa_StreamTime( stream );
streamTime = Pa_GetStreamTime( stream );
outTime = data->outTime;
if( outTime < 0.0 )
{
@ -119,7 +117,7 @@ static void ReportStreamTime( PortAudioStream *stream, paTestData *data )
else
{
latency = outTime - streamTime;
printf("Stream time = %8.1f, outTime = %8.1f, latency = %8.1f\n",
printf("Stream time = %8.4f, outTime = %8.4f, latency = %8.4f\n",
streamTime, outTime, latency );
}
fflush(stdout);
@ -129,73 +127,78 @@ static void ReportStreamTime( PortAudioStream *stream, paTestData *data )
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
paTestData DATA;
int i;
int totalSamps;
paTestData data;
PaTime startTime;
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. */
data.left_phase = data.right_phase = 0;
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
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,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER, /* frames per buffer */
NUM_BUFFERS, /* number of buffers, if zero then use default minimum */
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&DATA );
&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);
data.outTime = -1.0; /* mark time for callback as undefined */
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
startTime = Pa_GetStreamTime( stream );
do
{
ReportStreamTime( stream, &DATA );
ReportStreamTime( stream, &data );
Pa_Sleep(100);
} while( Pa_StreamTime( stream ) < (totalSamps/2) );
} while( (Pa_GetStreamTime( stream ) - startTime) < (NUM_SECONDS/2) );
/* Stop sound until ENTER hit. */
/* Stop sound for 2 seconds. */
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
data.outTime = -1.0; /* mark time for callback as undefined */
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
startTime = Pa_GetStreamTime( stream );
printf("Play until sound is finished.\n"); fflush(stdout);
do
{
ReportStreamTime( stream, &DATA );
ReportStreamTime( stream, &data );
Pa_Sleep(100);
} while( Pa_StreamActive( stream ) );
} while( (Pa_GetStreamTime( stream ) - startTime) < (NUM_SECONDS/2) );
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" );

View file

@ -0,0 +1,162 @@
/** @file patest_start_stop.c
@brief Play a sine wave for several seconds
- start and stop the stream multiple times.
@author Ross Bencina <rossb@audiomulch.com>
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $Id$
*
* 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_GetDefaultOutputDevice() /* default output device */
#define NUM_SECONDS (3)
#define NUM_LOOPS (4)
#define SAMPLE_RATE (44100)
#define FRAMES_PER_BUFFER (400)
#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( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
paTestData *data = (paTestData*)userData;
float *out = (float*)outputBuffer;
unsigned long i;
(void) timeInfo; /* Prevent unused variable warnings. */
(void) statusFlags;
(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 paContinue;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters outputParameters;
PaStream *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;
outputParameters.device = OUTPUT_DEVICE;
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&data );
if( err != paNoError ) goto error;
for( i=0; i<NUM_LOOPS; i++ )
{
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;
printf("Stopped.\n" );
Pa_Sleep( 1000 );
}
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

@ -1,17 +1,19 @@
/** @file patest_stop.c
@brief Test different ways of stopping audio.
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>
*/
/*
* $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
@ -44,12 +46,12 @@
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID())
#define OUTPUT_DEVICE (Pa_GetDefaultOutputDevice())
#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 LATENCY_SECONDS (3.f)
#define FRAMES_PER_NOTE (SAMPLE_RATE/2)
#define MAX_REPEATS (2)
#define FUNDAMENTAL (400.0f / SAMPLE_RATE)
@ -65,9 +67,10 @@
#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 waveform[TABLE_SIZE + 1]; /* Add one for guard point for interpolation. */
float phase_increment;
float phase;
float *tune;
@ -75,14 +78,16 @@ typedef struct
int frameCounter;
int noteCounter;
int repeatCounter;
PaTimestamp outTime;
PaTime 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.
@ -97,23 +102,30 @@ float LookupWaveform( paTestData *data, float phase )
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 )
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
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;
int finished = paContinue;
data->outTime = outTime;
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
/* data->outTime = outTime; */
if( !data->done )
{
for( i=0; i<framesPerBuffer; i++ )
@ -134,7 +146,7 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
data->done = 1;
if( data->stopMode == MODE_FINISH )
{
finished = 1;
finished = paComplete;
break;
}
}
@ -162,92 +174,103 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
int main(void);
int main(void)
{
paTestData DATA;
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 );
printf("PortAudio Test: play song and test stopping. ask for %f seconds latency\n", LATENCY_SECONDS );
/* initialise sinusoidal wavetable */
for( i=0; i<TABLE_SIZE; i++ )
{
DATA.waveform[i] = (float) (
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);
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("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("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("Should hear last repetition cut short by %f seconds.\n", LATENCY_SECONDS);
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;
PaStreamParameters outputParameters;
PaStream *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;
outputParameters.device = OUTPUT_DEVICE;
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = LATENCY_SECONDS;
outputParameters.hostApiSpecificStreamInfo = NULL;
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,
NULL, /* no input */
&outputParameters,
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 ) )
while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
{
/*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
data->noteCounter, data->repeatCounter );
fflush(stdout); /**/
fflush(stdout); */
Pa_Sleep( SLEEP_DUR );
}
if( err < 0 ) goto error;
}
else
{
@ -255,10 +278,11 @@ int TestStopMode( paTestData *data )
{
/*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime,
data->noteCounter, data->repeatCounter );
fflush(stdout); /**/
fflush(stdout); */
Pa_Sleep( SLEEP_DUR );
}
}
if( data->stopMode == MODE_ABORT )
{
printf("Call Pa_AbortStream()\n");
@ -270,12 +294,16 @@ int TestStopMode( paTestData *data )
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" );

View file

@ -0,0 +1,432 @@
/** @file patest_stop_playout.c
@brief Test whether all queued samples are played when Pa_StopStream()
is used with a callback or read/write stream, or when the callback
returns paComplete.
@author Ross Bencina <rossb@audiomulch.com>
*/
/*
* $Id$
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com/
* Copyright (c) 1999-2004 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)
#define TONE_SECONDS (1) /* long tone */
#define TONE_FADE_SECONDS (.04) /* fades at start and end of long tone */
#define GAP_SECONDS (.25) /* gap between long tone and blip */
#define BLIP_SECONDS (.035) /* short blip */
#define NUM_REPEATS (3)
#ifndef M_PI
#define M_PI (3.14159265)
#endif
#define TABLE_SIZE (2048)
typedef struct
{
float sine[TABLE_SIZE+1];
int repeatCount;
double phase;
double lowIncrement, highIncrement;
int gap1Length, toneLength, toneFadesLength, gap2Length, blipLength;
int gap1Countdown, toneCountdown, gap2Countdown, blipCountdown;
}
TestData;
static void RetriggerTestSignalGenerator( TestData *data )
{
data->phase = 0.;
data->gap1Countdown = data->gap1Length;
data->toneCountdown = data->toneLength;
data->gap2Countdown = data->gap2Length;
data->blipCountdown = data->blipLength;
}
static void ResetTestSignalGenerator( TestData *data )
{
data->repeatCount = 0;
RetriggerTestSignalGenerator( data );
}
static void InitTestSignalGenerator( TestData *data )
{
int signalLengthModBufferLength, 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]; /* guard point for linear interpolation */
data->lowIncrement = (330. / SAMPLE_RATE) * TABLE_SIZE;
data->highIncrement = (1760. / SAMPLE_RATE) * TABLE_SIZE;
data->gap1Length = GAP_SECONDS * SAMPLE_RATE;
data->toneLength = TONE_SECONDS * SAMPLE_RATE;
data->toneFadesLength = TONE_FADE_SECONDS * SAMPLE_RATE;
data->gap2Length = GAP_SECONDS * SAMPLE_RATE;
data->blipLength = BLIP_SECONDS * SAMPLE_RATE;
/* adjust signal length to be a multiple of the buffer length */
signalLengthModBufferLength = (data->gap1Length + data->toneLength + data->gap2Length + data->blipLength) % FRAMES_PER_BUFFER;
if( signalLengthModBufferLength > 0 )
data->toneLength += signalLengthModBufferLength;
ResetTestSignalGenerator( data );
}
#define MIN( a, b ) (((a)<(b))?(a):(b))
static void GenerateTestSignal( TestData *data, float *stereo, int frameCount )
{
int framesGenerated = 0;
float output;
long index;
float fraction;
int count, i;
while( framesGenerated < frameCount && data->repeatCount < NUM_REPEATS )
{
if( framesGenerated < frameCount && data->gap1Countdown > 0 ){
count = MIN( frameCount - framesGenerated, data->gap1Countdown );
for( i=0; i < count; ++i )
{
*stereo++ = 0.f;
*stereo++ = 0.f;
}
data->gap1Countdown -= count;
framesGenerated += count;
}
if( framesGenerated < frameCount && data->toneCountdown > 0 ){
count = MIN( frameCount - framesGenerated, data->toneCountdown );
for( i=0; i < count; ++i )
{
/* tone with data->lowIncrement phase increment */
index = (long)data->phase;
fraction = data->phase - index;
output = data->sine[ index ] + (data->sine[ index + 1 ] - data->sine[ index ]) * fraction;
data->phase += data->lowIncrement;
while( data->phase >= TABLE_SIZE )
data->phase -= TABLE_SIZE;
/* apply fade to ends */
if( data->toneCountdown < data->toneFadesLength )
{
/* cosine-bell fade out at end */
output *= (-cos(((float)data->toneCountdown / (float)data->toneFadesLength) * M_PI) + 1.) * .5;
}
else if( data->toneCountdown > data->toneLength - data->toneFadesLength )
{
/* cosine-bell fade in at start */
output *= (cos(((float)(data->toneCountdown - (data->toneLength - data->toneFadesLength)) / (float)data->toneFadesLength) * M_PI) + 1.) * .5;
}
output *= .5; /* play tone half as loud as blip */
*stereo++ = output;
*stereo++ = output;
data->toneCountdown--;
}
framesGenerated += count;
}
if( framesGenerated < frameCount && data->gap2Countdown > 0 ){
count = MIN( frameCount - framesGenerated, data->gap2Countdown );
for( i=0; i < count; ++i )
{
*stereo++ = 0.f;
*stereo++ = 0.f;
}
data->gap2Countdown -= count;
framesGenerated += count;
}
if( framesGenerated < frameCount && data->blipCountdown > 0 ){
count = MIN( frameCount - framesGenerated, data->blipCountdown );
for( i=0; i < count; ++i )
{
/* tone with data->highIncrement phase increment */
index = (long)data->phase;
fraction = data->phase - index;
output = data->sine[ index ] + (data->sine[ index + 1 ] - data->sine[ index ]) * fraction;
data->phase += data->highIncrement;
while( data->phase >= TABLE_SIZE )
data->phase -= TABLE_SIZE;
/* cosine-bell envelope over whole blip */
output *= (-cos( ((float)data->blipCountdown / (float)data->blipLength) * 2. * M_PI) + 1.) * .5;
*stereo++ = output;
*stereo++ = output;
data->blipCountdown--;
}
framesGenerated += count;
}
if( data->blipCountdown == 0 )
{
RetriggerTestSignalGenerator( data );
data->repeatCount++;
}
}
if( framesGenerated < frameCount )
{
count = frameCount - framesGenerated;
for( i=0; i < count; ++i )
{
*stereo++ = 0.f;
*stereo++ = 0.f;
}
}
}
static int IsTestSignalFinished( TestData *data )
{
if( data->repeatCount >= NUM_REPEATS )
return 1;
else
return 0;
}
static int TestCallback1( const void *inputBuffer, void *outputBuffer,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
GenerateTestSignal( (TestData*)userData, (float*)outputBuffer, frameCount );
if( IsTestSignalFinished( (TestData*)userData ) )
return paComplete;
else
return paContinue;
}
volatile int testCallback2Finished = 0;
static int TestCallback2( const void *inputBuffer, void *outputBuffer,
unsigned long frameCount,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *userData )
{
(void) inputBuffer; /* Prevent unused variable warnings. */
(void) timeInfo;
(void) statusFlags;
GenerateTestSignal( (TestData*)userData, (float*)outputBuffer, frameCount );
if( IsTestSignalFinished( (TestData*)userData ) )
testCallback2Finished = 1;
return paContinue;
}
/*******************************************************************/
int main(void);
int main(void)
{
PaStreamParameters outputParameters;
PaStream *stream;
PaError err;
TestData data;
float writeBuffer[ FRAMES_PER_BUFFER * 2 ];
printf("PortAudio Test: check that stopping stream plays out all queued samples. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER);
InitTestSignalGenerator( &data );
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultHighOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
/* test paComplete ---------------------------------------------------------- */
ResetTestSignalGenerator( &data );
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
TestCallback1,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("\nPlaying 'tone-blip' %d times using callback, stops by returning paComplete from callback.\n", NUM_REPEATS );
printf("If final blip is not intact, callback+paComplete implementation may be faulty.\n\n" );
while( (err = Pa_IsStreamActive( stream )) == 1 )
Pa_Sleep( 5 );
if( err != 0 ) goto error;
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Sleep( 500 );
/* test Pa_StopStream() with callback --------------------------------------- */
ResetTestSignalGenerator( &data );
testCallback2Finished = 0;
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
TestCallback2,
&data );
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("\nPlaying 'tone-blip' %d times using callback, stops by calling Pa_StopStream.\n", NUM_REPEATS );
printf("If final blip is not intact, callback+Pa_StopStream implementation may be faulty.\n\n" );
/* note that polling a volatile flag is not a good way to synchronise with
the callback, but it's the best we can do portably. */
while( !testCallback2Finished )
Pa_Sleep( 2 );
err = Pa_StopStream( stream );
if( err != paNoError ) goto error;
err = Pa_CloseStream( stream );
if( err != paNoError ) goto error;
Pa_Sleep( 500 );
/* test Pa_StopStream() with Pa_WriteStream --------------------------------- */
ResetTestSignalGenerator( &data );
err = Pa_OpenStream(
&stream,
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
NULL, /* no callback, use blocking API */
NULL ); /* no callback, so no callback userData */
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
printf("\nPlaying 'tone-blip' %d times using Pa_WriteStream, stops by calling Pa_StopStream.\n", NUM_REPEATS );
printf("If final blip is not intact, Pa_WriteStream+Pa_StopStream implementation may be faulty.\n\n" );
do{
GenerateTestSignal( &data, writeBuffer, FRAMES_PER_BUFFER );
err = Pa_WriteStream( stream, writeBuffer, FRAMES_PER_BUFFER );
if( err != paNoError ) goto error;
}while( !IsTestSignalFinished( &data ) );
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

@ -1,19 +1,21 @@
/** @file patest_sync.c
@brief 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 PaUtil_GetTime().
-# Foreground outputs video (printf) in sync with audio.
-# Repeat.
@author Phil Burk http://www.softsynth.com
*/
/*
* $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
@ -46,43 +48,49 @@
#include <stdio.h>
#include <math.h>
#include "portaudio.h"
#include "pa_util.h"
#define NUM_BEEPS (6)
#define SAMPLE_RATE (44100)
#define SAMPLE_PERIOD (1.0/44100.0)
#define FRAMES_PER_BUFFER (256)
#define BEEP_DURATION (1000)
#define BEEP_DURATION (400)
#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)
#define TIMEOUT_MSEC (15000)
#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;
volatile int requestBeep; /* Set by foreground, cleared by background. */
PaTime beepTime;
int beepCount;
double latency; /* For debugging. */
}
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. */
static unsigned long randSeed = 99887766; /* 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,
static int patestCallback( const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
PaTimestamp outTime, void *userData )
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags statusFlags, void *userData )
{
/* Cast data passed through stream to our structure. */
paTestData *data = (paTestData*)userData;
@ -90,6 +98,8 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
unsigned int i;
(void) inputBuffer;
data->latency = timeInfo->outputBufferDacTime - timeInfo->currentTime;
for( i=0; i<framesPerBuffer; i++ )
{
switch( data->state )
@ -99,27 +109,29 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
if( data->requestBeep )
{
int random = GenerateRandomNumber() >> 14;
data->beepTime = outTime + (i + random + (SAMPLE_RATE/4));
data->beepTime = timeInfo->outputBufferDacTime + (( (double)(random + SAMPLE_RATE)) * SAMPLE_PERIOD );
data->state = STATE_BKG_PENDING;
data->requestBeep = 0;
}
*out++ = 0.0; /* left */
*out++ = 0.0; /* right */
break;
case STATE_BKG_PENDING:
if( (timeInfo->outputBufferDacTime + (i*SAMPLE_PERIOD)) >= data->beepTime )
{
data->state = STATE_BKG_BEEPING;
data->beepCount = BEEP_DURATION;
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;
data->requestBeep = 0;
*out++ = 0.0; /* left */
*out++ = 0.0; /* right */
}
@ -138,6 +150,7 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
}
data->beepCount -= 1;
break;
default:
data->state = STATE_BKG_IDLE;
break;
@ -149,11 +162,12 @@ static int patestCallback( void *inputBuffer, void *outputBuffer,
int main(void);
int main(void)
{
PortAudioStream *stream;
PaStream *stream;
PaError err;
paTestData DATA;
int i, timeout;
PaTimestamp previousTime;
PaTime previousTime;
PaStreamParameters outputParameters;
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 );
@ -164,25 +178,37 @@ int main(void)
/* Initialize library before making any other calls. */
err = Pa_Initialize();
if( err != paNoError ) goto error;
outputParameters.device = Pa_GetDefaultOutputDevice();
outputParameters.channelCount = 2;
outputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.sampleFormat = paFloat32;
outputParameters.suggestedLatency = (double)LATENCY_MSEC / 1000;
/* Open an audio I/O stream. */
err = Pa_OpenDefaultStream(
err = Pa_OpenStream(
&stream,
0, /* no input channels */
2, /* stereo output */
paFloat32, /* 32 bit floating point output */
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
NUM_BUFFERS,
FRAMES_PER_BUFFER, /* frames per buffer */
paClipOff, /* we won't output out of range samples so don't bother clipping them */
patestCallback,
&DATA );
if( err != paNoError ) goto error;
if( err != paNoError ) goto error;
err = Pa_StartStream( stream );
if( err != paNoError ) goto error;
previousTime = Pa_StreamTime( stream );
printf("started\n");
fflush(stdout);
previousTime = Pa_GetStreamTime( 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);
@ -191,30 +217,36 @@ int main(void)
fprintf( stderr, "Timed out waiting for background to acknowledge request.\n" );
goto error;
}
printf("calc beep for %9.3f, latency = %6.3f\n", DATA.beepTime, DATA.latency );
fflush(stdout);
/* Wait for scheduled beep time. */
timeout = TIMEOUT_MSEC + (10000/SLEEP_MSEC);
while( (Pa_StreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) )
while( (Pa_GetStreamTime( 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 );
fprintf( stderr, "Timed out waiting for time. Now = %9.3f, Beep for %9.3f.\n",
PaUtil_GetTime(), 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) );
printf("hear \"BEEP\" at %9.3f, delta = %9.3f\n",
Pa_GetStreamTime( stream ), (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;

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