aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pd/portaudio/LICENSE.txt65
-rw-r--r--pd/portaudio/Makefile.in177
-rw-r--r--pd/portaudio/README.txt81
-rw-r--r--pd/portaudio/V19-devel-readme.txt222
-rwxr-xr-xpd/portaudio/fixdir.bat19
-rwxr-xr-xpd/portaudio/fixfile.bat7
-rwxr-xr-xpd/portaudio/install-sh251
-rw-r--r--pd/portaudio/pa_asio/ASIO-README.txt137
-rw-r--r--pd/portaudio/pa_asio/Callback_adaptation_.pdfbin0 -> 50527 bytes
-rw-r--r--pd/portaudio/pa_asio/Pa_ASIO.pdfbin0 -> 50778 bytes
-rw-r--r--pd/portaudio/pa_asio/iasiothiscallresolver.cpp563
-rw-r--r--pd/portaudio/pa_asio/iasiothiscallresolver.h197
-rw-r--r--pd/portaudio/pa_asio/pa_asio.cpp2656
-rw-r--r--pd/portaudio/pa_asio/pa_asio.h79
-rw-r--r--pd/portaudio/pa_common/pa_allocation.c224
-rw-r--r--pd/portaudio/pa_common/pa_allocation.h95
-rw-r--r--pd/portaudio/pa_common/pa_converters.c1925
-rw-r--r--pd/portaudio/pa_common/pa_converters.h254
18 files changed, 6952 insertions, 0 deletions
diff --git a/pd/portaudio/LICENSE.txt b/pd/portaudio/LICENSE.txt
new file mode 100644
index 00000000..105da3f7
--- /dev/null
+++ b/pd/portaudio/LICENSE.txt
@@ -0,0 +1,65 @@
+Portable header file to contain:
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * PortAudio API Header File
+ * Latest version available at: http://www.audiomulch.com/portaudio/
+ *
+ * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
+Implementation files to contain:
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest version at: http://www.audiomulch.com/portaudio/
+ * <platform> Implementation
+ * Copyright (c) 1999-2000 <author(s)>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */ \ No newline at end of file
diff --git a/pd/portaudio/Makefile.in b/pd/portaudio/Makefile.in
new file mode 100644
index 00000000..841aedb9
--- /dev/null
+++ b/pd/portaudio/Makefile.in
@@ -0,0 +1,177 @@
+#
+# PortAudio V19 Makefile.in
+#
+# Dominic Mazzoni
+#
+
+PREFIX = @prefix@
+CC = @CC@
+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.19
+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_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/$(PADLLV) tests
+
+tests: bin/ $(TESTS)
+
+lib/$(PALIB): lib/ $(OBJS) Makefile $(PAINC)
+ $(AR) ruv lib/$(PALIB) $(OBJS)
+ $(RANLIB) lib/$(PALIB)
+
+lib/$(PADLLV): lib/ $(OBJS) Makefile $(PAINC)
+ $(CC) $(SHARED_FLAGS) -o lib/$(PADLLV) $(OBJS) $(DLL_LIBS)
+
+$(TESTS): bin/%: lib/$(PALIB) Makefile $(PAINC) pa_tests/%.c
+ $(CC) -o $@ $(CFLAGS) pa_tests/$*.c lib/$(PALIB) $(LIBS)
+
+install: lib/$(PALIB) lib/$(PADLLV)
+ $(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) -m 644 pa_common/portaudio.h $(PREFIX)/include/portaudio.h
+ @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 ""
+
+clean:
+ 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
+
+
+
diff --git a/pd/portaudio/README.txt b/pd/portaudio/README.txt
new file mode 100644
index 00000000..4cfc6166
--- /dev/null
+++ b/pd/portaudio/README.txt
@@ -0,0 +1,81 @@
+README for PortAudio
+Implementations for PC DirectSound and Mac SoundManager
+
+/*
+ * PortAudio Portable Real-Time Audio Library
+ * Latest Version at: http://www.portaudio.com//
+ *
+ * Copyright (c) 1999-2000 Phil Burk and Ross Bencina
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * Any person wishing to distribute modifications to the Software is
+ * requested to send the modifications to the original developer so that
+ * they can be incorporated into the canonical version.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+PortAudio is a portable audio I/O library designed for cross-platform
+support of audio. It uses a callback mechanism to request audio processing.
+Audio can be generated in various formats, including 32 bit floating point,
+and will be converted to the native format internally.
+
+Documentation:
+ See "pa_common/portaudio.h" for API spec.
+ See docs folder for a tutorial.
+ Also see http://www.portaudio.com/docs/
+ And see "pa_tests/patest_saw.c" for an example.
+
+For information on compiling programs with PortAudio, please see the
+tutorial at:
+
+ http://www.portaudio.com/docs/pa_tutorial.html
+
+Important Files and Folders:
+ pa_common/ = platform independant code
+ pa_common/portaudio.h = header file for PortAudio API. Specifies API.
+ pa_common/pa_lib.c = host independant code for all implementations.
+
+ pablio = simple blocking read/write interface
+
+Platform Implementations
+ pa_asio = ASIO for Windows and Macintosh
+ pa_beos = BeOS
+ pa_mac_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
+ pa_win_ds = Windows Direct Sound
+ pa_win_wmme = Windows MME (most widely supported)
+
+Test Programs
+ pa_tests/pa_fuzz.c = guitar fuzz box
+ pa_tests/pa_devs.c = print a list of available devices
+ pa_tests/pa_minlat.c = determine minimum latency for your machine
+ pa_tests/paqa_devs.c = self test that opens all devices
+ pa_tests/paqa_errs.c = test error detection and reporting
+ pa_tests/patest_clip.c = hear a sine wave clipped and unclipped
+ pa_tests/patest_dither.c = hear effects of dithering (extremely subtle)
+ pa_tests/patest_pink.c = fun with pink noise
+ pa_tests/patest_record.c = record and playback some audio
+ pa_tests/patest_maxsines.c = how many sine waves can we play? Tests Pa_GetCPULoad().
+ pa_tests/patest_sine.c = output a sine wave in a simple PA app
+ pa_tests/patest_sync.c = test syncronization of audio and video
+ pa_tests/patest_wire.c = pass input to output, wire simulator
diff --git a/pd/portaudio/V19-devel-readme.txt b/pd/portaudio/V19-devel-readme.txt
new file mode 100644
index 00000000..ae5570eb
--- /dev/null
+++ b/pd/portaudio/V19-devel-readme.txt
@@ -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
+
diff --git a/pd/portaudio/fixdir.bat b/pd/portaudio/fixdir.bat
new file mode 100755
index 00000000..92d6c747
--- /dev/null
+++ b/pd/portaudio/fixdir.bat
@@ -0,0 +1,19 @@
+rem Use Astyle to fix style in 'C' files
+cd %1%
+
+fixlines -p *.c
+fixlines -p *.cpp
+fixlines -p *.cc
+
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.c
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cpp
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor *.cc
+del *.orig
+@rem convert line terminators to Unix style LFs
+fixlines -u *.c
+fixlines -u *.cpp
+fixlines -u *.cc
+fixlines -u *.h
+del *.bak
+
+cd ..\
diff --git a/pd/portaudio/fixfile.bat b/pd/portaudio/fixfile.bat
new file mode 100755
index 00000000..48f6fbc2
--- /dev/null
+++ b/pd/portaudio/fixfile.bat
@@ -0,0 +1,7 @@
+rem Use Astyle to fix style in a file
+fixlines -p %1%
+astyle --style=ansi -c -o --convert-tabs --indent-preprocessor %1%
+del %1%.orig
+@rem convert line terminators to Unix style LFs
+fixlines -u %1%
+del %1%.bak
diff --git a/pd/portaudio/install-sh b/pd/portaudio/install-sh
new file mode 100755
index 00000000..e9de2384
--- /dev/null
+++ b/pd/portaudio/install-sh
@@ -0,0 +1,251 @@
+#!/bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ chmodcmd=""
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/pd/portaudio/pa_asio/ASIO-README.txt b/pd/portaudio/pa_asio/ASIO-README.txt
new file mode 100644
index 00000000..9fb74ae1
--- /dev/null
+++ b/pd/portaudio/pa_asio/ASIO-README.txt
@@ -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;
+}
+
+
+---
diff --git a/pd/portaudio/pa_asio/Callback_adaptation_.pdf b/pd/portaudio/pa_asio/Callback_adaptation_.pdf
new file mode 100644
index 00000000..76bf6786
--- /dev/null
+++ b/pd/portaudio/pa_asio/Callback_adaptation_.pdf
Binary files differ
diff --git a/pd/portaudio/pa_asio/Pa_ASIO.pdf b/pd/portaudio/pa_asio/Pa_ASIO.pdf
new file mode 100644
index 00000000..ac5ecadb
--- /dev/null
+++ b/pd/portaudio/pa_asio/Pa_ASIO.pdf
Binary files differ
diff --git a/pd/portaudio/pa_asio/iasiothiscallresolver.cpp b/pd/portaudio/pa_asio/iasiothiscallresolver.cpp
new file mode 100644
index 00000000..8dfefbd6
--- /dev/null
+++ b/pd/portaudio/pa_asio/iasiothiscallresolver.cpp
@@ -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 */
+
diff --git a/pd/portaudio/pa_asio/iasiothiscallresolver.h b/pd/portaudio/pa_asio/iasiothiscallresolver.h
new file mode 100644
index 00000000..2ecfed79
--- /dev/null
+++ b/pd/portaudio/pa_asio/iasiothiscallresolver.h
@@ -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 */
+
+
diff --git a/pd/portaudio/pa_asio/pa_asio.cpp b/pd/portaudio/pa_asio/pa_asio.cpp
new file mode 100644
index 00000000..34a2d39b
--- /dev/null
+++ b/pd/portaudio/pa_asio/pa_asio.cpp
@@ -0,0 +1,2656 @@
+/*
+ * $Id: pa_asio.cpp,v 1.7.2.59 2004/02/15 15:39:05 rossbencina Exp $
+ * Portable Audio I/O Library for ASIO Drivers
+ *
+ * Author: Stephane Letz
+ * Based on the Open Source API proposed by Ross Bencina
+ * Copyright (c) 2000-2002 Stephane Letz, 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.
+ */
+
+/* Modification History
+
+ 08-03-01 First version : Stephane Letz
+ 08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk
+ 08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz
+ 08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz
+ 08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil
+ 08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when
+ the stream is stopped : Stephane Letz
+ 08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when
+ the stream is stopped : Stephane Letz
+ 10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that
+ respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz
+ 10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz
+ 10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil
+ 10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk
+ 10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz
+ 11-06-01 Rename functions : Stephane Letz
+ 11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz
+ 11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk
+ 01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz
+ 02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz
+ 19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz
+ 09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz
+ 12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk
+ 13-04-02 Removes another compiler warning : Stephane Letz
+ 30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz
+ 12-06-02 Rehashed into new multi-api infrastructure, added support for all ASIO sample formats : Ross Bencina
+ 18-06-02 Added pa_asio.h, PaAsio_GetAvailableLatencyValues() : Ross B.
+ 21-06-02 Added SelectHostBufferSize() which selects host buffer size based on user latency parameters : Ross Bencina
+ ** NOTE maintanance history is now stored in CVS **
+*/
+
+/** @file
+
+ Note that specific support for paInputUnderflow, paOutputOverflow and
+ paNeverDropInput is not necessary or possible with this driver due to the
+ synchronous full duplex double-buffered architecture of ASIO.
+
+ @todo check that CoInitialize()/CoUninitialize() are always correctly
+ paired, even in error cases.
+
+ @todo implement host api specific extension to set i/o buffer sizes in frames
+
+ @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
+
+ @todo implement IsFormatSupported
+
+ @todo work out how to implement stream stoppage from callback and
+ implement IsStreamActive properly. Stream stoppage could be implemented
+ using a high-priority thread blocked on an Event which is signalled
+ by the callback. Or, we could just call ASIO stop from the callback
+ and see what happens.
+
+ @todo rigorously check asio return codes and convert to pa error codes
+
+ @todo Different channels of a multichannel stream can have different sample
+ formats, but we assume that all are the same as the first channel for now.
+ Fixing this will require the block processor to maintain per-channel
+ conversion functions - could get nasty.
+
+ @todo investigate whether the asio processNow flag needs to be honoured
+
+ @todo handle asioMessages() callbacks in a useful way, or at least document
+ what cases we don't handle.
+
+ @todo miscellaneous other FIXMEs
+
+ @todo provide an asio-specific method for setting the systems specific
+ value (aka main window handle) - check that this matches the value
+ passed to PaAsio_ShowControlPanel, or remove it entirely from
+ PaAsio_ShowControlPanel. - this would allow PaAsio_ShowControlPanel
+ to be called for the currently open stream (at present all streams
+ must be closed).
+*/
+
+
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+//#include <values.h>
+
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "portaudio.h"
+#include "pa_asio.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"
+
+#include "asiosys.h"
+#include "asio.h"
+#include "asiodrivers.h"
+#include "iasiothiscallresolver.h"
+
+/*
+#if MAC
+#include <Devices.h>
+#include <Timer.h>
+#include <Math64.h>
+#else
+*/
+/*
+#include <math.h>
+#include <windows.h>
+#include <mmsystem.h>
+*/
+/*
+#endif
+*/
+
+/* external references */
+extern AsioDrivers* asioDrivers ;
+bool loadAsioDriver(char *name);
+
+
+/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
+/* not tested at all since new code was introduced. */
+#define CARBON_COMPATIBLE (0)
+
+
+
+
+/* prototypes for functions declared in this file */
+
+extern "C" PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex );
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+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 IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+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 );
+
+/* our ASIO callback functions */
+
+static void bufferSwitch(long index, ASIOBool processNow);
+static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
+static void sampleRateChanged(ASIOSampleRate sRate);
+static long asioMessages(long selector, long value, void* message, double* opt);
+
+static ASIOCallbacks asioCallbacks_ =
+ { bufferSwitch, sampleRateChanged, asioMessages, bufferSwitchTimeInfo };
+
+
+#define PA_ASIO_SET_LAST_HOST_ERROR( errorCode, errorText ) \
+ PaUtil_SetLastHostErrorInfo( paASIO, errorCode, errorText )
+
+
+static void PaAsio_SetLastSystemError( DWORD errorCode )
+{
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ PaUtil_SetLastHostErrorInfo( paASIO, errorCode, (const char*)lpMsgBuf );
+ LocalFree( lpMsgBuf );
+}
+
+#define PA_ASIO_SET_LAST_SYSTEM_ERROR( errorCode ) \
+ PaAsio_SetLastSystemError( errorCode )
+
+
+static const char* PaAsio_GetAsioErrorText( ASIOError asioError )
+{
+ const char *result;
+
+ switch( asioError ){
+ case ASE_OK:
+ case ASE_SUCCESS: result = "Success"; break;
+ case ASE_NotPresent: result = "Hardware input or output is not present or available"; break;
+ case ASE_HWMalfunction: result = "Hardware is malfunctioning"; break;
+ case ASE_InvalidParameter: result = "Input parameter invalid"; break;
+ case ASE_InvalidMode: result = "Hardware is in a bad mode or used in a bad mode"; break;
+ case ASE_SPNotAdvancing: result = "Hardware is not running when sample position is inquired"; break;
+ case ASE_NoClock: result = "Sample clock or rate cannot be determined or is not present"; break;
+ case ASE_NoMemory: result = "Not enough memory for completing the request"; break;
+ default: result = "Unknown ASIO error"; break;
+ }
+
+ return result;
+}
+
+
+#define PA_ASIO_SET_LAST_ASIO_ERROR( asioError ) \
+ PaUtil_SetLastHostErrorInfo( paASIO, asioError, PaAsio_GetAsioErrorText( asioError ) )
+
+
+
+
+// Atomic increment and decrement operations
+#if MAC
+ /* need to be implemented on Mac */
+ inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));}
+ inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));}
+#elif WINDOWS
+ inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));}
+ inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));}
+#endif
+
+
+
+typedef struct PaAsioDriverInfo
+{
+ ASIODriverInfo asioDriverInfo;
+ long inputChannelCount, outputChannelCount;
+ long bufferMinSize, bufferMaxSize, bufferPreferredSize, bufferGranularity;
+ bool postOutput;
+}
+PaAsioDriverInfo;
+
+
+/* PaAsioHostApiRepresentation - host api datastructure specific to this implementation */
+
+typedef struct
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+
+ void *systemSpecific;
+
+ /* the ASIO C API only allows one ASIO driver to be open at a time,
+ so we kee track of whether we have the driver open here, and
+ use this information to return errors from OpenStream if the
+ driver is already open.
+
+ openAsioDeviceIndex will be PaNoDevice if there is no device open
+ and a valid pa_asio (not global) device index otherwise.
+
+ openAsioDriverInfo is populated with the driver info for the
+ currently open device (if any)
+ */
+ PaDeviceIndex openAsioDeviceIndex;
+ PaAsioDriverInfo openAsioDriverInfo;
+}
+PaAsioHostApiRepresentation;
+
+
+/*
+ Retrieve <driverCount> driver names from ASIO, returned in a char**
+ allocated in <group>.
+*/
+static char **GetAsioDriverNames( PaUtilAllocationGroup *group, long driverCount )
+{
+ char **result = 0;
+ int i;
+
+ result =(char**)PaUtil_GroupAllocateMemory(
+ group, sizeof(char*) * driverCount );
+ if( !result )
+ goto error;
+
+ result[0] = (char*)PaUtil_GroupAllocateMemory(
+ group, 32 * driverCount );
+ if( !result[0] )
+ goto error;
+
+ for( i=0; i<driverCount; ++i )
+ result[i] = result[0] + (32 * i);
+
+ asioDrivers->getDriverNames( result, driverCount );
+
+error:
+ return result;
+}
+
+
+static PaSampleFormat AsioSampleTypeToPaNativeSampleFormat(ASIOSampleType type)
+{
+ switch (type) {
+ case ASIOSTInt16MSB:
+ case ASIOSTInt16LSB:
+ return paInt16;
+
+ case ASIOSTFloat32MSB:
+ case ASIOSTFloat32LSB:
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB:
+ return paFloat32;
+
+ case ASIOSTInt32MSB:
+ case ASIOSTInt32LSB:
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32LSB16:
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32MSB24:
+ case ASIOSTInt32LSB18:
+ case ASIOSTInt32LSB20:
+ case ASIOSTInt32LSB24:
+ return paInt32;
+
+ case ASIOSTInt24MSB:
+ case ASIOSTInt24LSB:
+ return paInt24;
+
+ default:
+ return paCustomFormat;
+ }
+}
+
+
+static int BytesPerAsioSample( ASIOSampleType sampleType )
+{
+ switch (sampleType) {
+ case ASIOSTInt16MSB:
+ case ASIOSTInt16LSB:
+ return 2;
+
+ case ASIOSTFloat64MSB:
+ case ASIOSTFloat64LSB:
+ return 8;
+
+ case ASIOSTFloat32MSB:
+ case ASIOSTFloat32LSB:
+ case ASIOSTInt32MSB:
+ case ASIOSTInt32LSB:
+ case ASIOSTInt32MSB16:
+ case ASIOSTInt32LSB16:
+ case ASIOSTInt32MSB18:
+ case ASIOSTInt32MSB20:
+ case ASIOSTInt32MSB24:
+ case ASIOSTInt32LSB18:
+ case ASIOSTInt32LSB20:
+ case ASIOSTInt32LSB24:
+ return 4;
+
+ case ASIOSTInt24MSB:
+ case ASIOSTInt24LSB:
+ return 3;
+
+ default:
+ return 0;
+ }
+}
+
+
+static void Swap16( void *buffer, long shift, long count )
+{
+ unsigned short *p = (unsigned short*)buffer;
+ unsigned short temp;
+ (void) shift; /* unused parameter */
+
+ while( count-- )
+ {
+ temp = *p;
+ *p++ = (unsigned short)((temp<<8) | (temp>>8));
+ }
+}
+
+static void Swap24( void *buffer, long shift, long count )
+{
+ unsigned char *p = (unsigned char*)buffer;
+ unsigned char temp;
+ (void) shift; /* unused parameter */
+
+ while( count-- )
+ {
+ temp = *p;
+ *p = *(p+2);
+ *(p+2) = temp;
+ p += 3;
+ }
+}
+
+#define PA_SWAP32_( x ) ((x>>24) | ((x>>8)&0xFF00) | ((x<<8)&0xFF0000) | (x<<24));
+
+static void Swap32( void *buffer, long shift, long count )
+{
+ unsigned long *p = (unsigned long*)buffer;
+ unsigned long temp;
+ (void) shift; /* unused parameter */
+
+ while( count-- )
+ {
+ temp = *p;
+ *p++ = PA_SWAP32_( temp);
+ }
+}
+
+static void SwapShiftLeft32( void *buffer, long shift, long count )
+{
+ unsigned long *p = (unsigned long*)buffer;
+ unsigned long temp;
+
+ while( count-- )
+ {
+ temp = *p;
+ temp = PA_SWAP32_( temp);
+ *p++ = temp << shift;
+ }
+}
+
+static void ShiftRightSwap32( void *buffer, long shift, long count )
+{
+ unsigned long *p = (unsigned long*)buffer;
+ unsigned long temp;
+
+ while( count-- )
+ {
+ temp = *p >> shift;
+ *p++ = PA_SWAP32_( temp);
+ }
+}
+
+static void ShiftLeft32( void *buffer, long shift, long count )
+{
+ unsigned long *p = (unsigned long*)buffer;
+ unsigned long temp;
+
+ while( count-- )
+ {
+ temp = *p;
+ *p++ = temp << shift;
+ }
+}
+
+static void ShiftRight32( void *buffer, long shift, long count )
+{
+ unsigned long *p = (unsigned long*)buffer;
+ unsigned long temp;
+
+ while( count-- )
+ {
+ temp = *p;
+ *p++ = temp >> shift;
+ }
+}
+
+#define PA_SWAP_( x, y ) temp=x; x = y; y = temp;
+
+static void Swap64ConvertFloat64ToFloat32( void *buffer, long shift, long count )
+{
+ double *in = (double*)buffer;
+ float *out = (float*)buffer;
+ unsigned char *p;
+ unsigned char temp;
+ (void) shift; /* unused parameter */
+
+ while( count-- )
+ {
+ p = (unsigned char*)in;
+ PA_SWAP_( p[0], p[7] );
+ PA_SWAP_( p[1], p[6] );
+ PA_SWAP_( p[2], p[5] );
+ PA_SWAP_( p[3], p[4] );
+
+ *out++ = (float) (*in++);
+ }
+}
+
+static void ConvertFloat64ToFloat32( void *buffer, long shift, long count )
+{
+ double *in = (double*)buffer;
+ float *out = (float*)buffer;
+ (void) shift; /* unused parameter */
+
+ while( count-- )
+ *out++ = (float) (*in++);
+}
+
+static void ConvertFloat32ToFloat64Swap64( void *buffer, long shift, long count )
+{
+ float *in = ((float*)buffer) + (count-1);
+ double *out = ((double*)buffer) + (count-1);
+ unsigned char *p;
+ unsigned char temp;
+ (void) shift; /* unused parameter */
+
+ while( count-- )
+ {
+ *out = *in--;
+
+ p = (unsigned char*)out;
+ PA_SWAP_( p[0], p[7] );
+ PA_SWAP_( p[1], p[6] );
+ PA_SWAP_( p[2], p[5] );
+ PA_SWAP_( p[3], p[4] );
+
+ out--;
+ }
+}
+
+static void ConvertFloat32ToFloat64( void *buffer, long shift, long count )
+{
+ float *in = ((float*)buffer) + (count-1);
+ double *out = ((double*)buffer) + (count-1);
+ (void) shift; /* unused parameter */
+
+ while( count-- )
+ *out-- = *in--;
+}
+
+#ifdef MAC
+#define PA_MSB_IS_NATIVE_
+#undef PA_LSB_IS_NATIVE_
+#endif
+
+#ifdef WINDOWS
+#undef PA_MSB_IS_NATIVE_
+#define PA_LSB_IS_NATIVE_
+#endif
+
+typedef void PaAsioBufferConverter( void *, long, long );
+
+static void SelectAsioToPaConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
+{
+ *shift = 0;
+ *converter = 0;
+
+ switch (type) {
+ case ASIOSTInt16MSB:
+ /* dest: paInt16, no conversion necessary, possible byte swap*/
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap16;
+ #endif
+ break;
+ case ASIOSTInt16LSB:
+ /* dest: paInt16, no conversion necessary, possible byte swap*/
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap16;
+ #endif
+ break;
+ case ASIOSTFloat32MSB:
+ /* dest: paFloat32, no conversion necessary, possible byte swap*/
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap32;
+ #endif
+ break;
+ case ASIOSTFloat32LSB:
+ /* dest: paFloat32, no conversion necessary, possible byte swap*/
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap32;
+ #endif
+ break;
+ case ASIOSTFloat64MSB:
+ /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap64ConvertFloat64ToFloat32;
+ #else
+ *converter = ConvertFloat64ToFloat32;
+ #endif
+ break;
+ case ASIOSTFloat64LSB:
+ /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap64ConvertFloat64ToFloat32;
+ #else
+ *converter = ConvertFloat64ToFloat32;
+ #endif
+ break;
+ case ASIOSTInt32MSB:
+ /* dest: paInt32, no conversion necessary, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap32;
+ #endif
+ break;
+ case ASIOSTInt32LSB:
+ /* dest: paInt32, no conversion necessary, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap32;
+ #endif
+ break;
+ case ASIOSTInt32MSB16:
+ /* dest: paInt32, 16 bit shift, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = SwapShiftLeft32;
+ #else
+ *converter = ShiftLeft32;
+ #endif
+ *shift = 16;
+ break;
+ case ASIOSTInt32MSB18:
+ /* dest: paInt32, 14 bit shift, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = SwapShiftLeft32;
+ #else
+ *converter = ShiftLeft32;
+ #endif
+ *shift = 14;
+ break;
+ case ASIOSTInt32MSB20:
+ /* dest: paInt32, 12 bit shift, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = SwapShiftLeft32;
+ #else
+ *converter = ShiftLeft32;
+ #endif
+ *shift = 12;
+ break;
+ case ASIOSTInt32MSB24:
+ /* dest: paInt32, 8 bit shift, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = SwapShiftLeft32;
+ #else
+ *converter = ShiftLeft32;
+ #endif
+ *shift = 8;
+ break;
+ case ASIOSTInt32LSB16:
+ /* dest: paInt32, 16 bit shift, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = SwapShiftLeft32;
+ #else
+ *converter = ShiftLeft32;
+ #endif
+ *shift = 16;
+ break;
+ case ASIOSTInt32LSB18:
+ /* dest: paInt32, 14 bit shift, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = SwapShiftLeft32;
+ #else
+ *converter = ShiftLeft32;
+ #endif
+ *shift = 14;
+ break;
+ case ASIOSTInt32LSB20:
+ /* dest: paInt32, 12 bit shift, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = SwapShiftLeft32;
+ #else
+ *converter = ShiftLeft32;
+ #endif
+ *shift = 12;
+ break;
+ case ASIOSTInt32LSB24:
+ /* dest: paInt32, 8 bit shift, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = SwapShiftLeft32;
+ #else
+ *converter = ShiftLeft32;
+ #endif
+ *shift = 8;
+ break;
+ case ASIOSTInt24MSB:
+ /* dest: paInt24, no conversion necessary, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap24;
+ #endif
+ break;
+ case ASIOSTInt24LSB:
+ /* dest: paInt24, no conversion necessary, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap24;
+ #endif
+ break;
+ }
+}
+
+
+static void SelectPaToAsioConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
+{
+ *shift = 0;
+ *converter = 0;
+
+ switch (type) {
+ case ASIOSTInt16MSB:
+ /* src: paInt16, no conversion necessary, possible byte swap*/
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap16;
+ #endif
+ break;
+ case ASIOSTInt16LSB:
+ /* src: paInt16, no conversion necessary, possible byte swap*/
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap16;
+ #endif
+ break;
+ case ASIOSTFloat32MSB:
+ /* src: paFloat32, no conversion necessary, possible byte swap*/
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap32;
+ #endif
+ break;
+ case ASIOSTFloat32LSB:
+ /* src: paFloat32, no conversion necessary, possible byte swap*/
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap32;
+ #endif
+ break;
+ case ASIOSTFloat64MSB:
+ /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = ConvertFloat32ToFloat64Swap64;
+ #else
+ *converter = ConvertFloat32ToFloat64;
+ #endif
+ break;
+ case ASIOSTFloat64LSB:
+ /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = ConvertFloat32ToFloat64Swap64;
+ #else
+ *converter = ConvertFloat32ToFloat64;
+ #endif
+ break;
+ case ASIOSTInt32MSB:
+ /* src: paInt32, no conversion necessary, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap32;
+ #endif
+ break;
+ case ASIOSTInt32LSB:
+ /* src: paInt32, no conversion necessary, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap32;
+ #endif
+ break;
+ case ASIOSTInt32MSB16:
+ /* src: paInt32, 16 bit shift, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = ShiftRightSwap32;
+ #else
+ *converter = ShiftRight32;
+ #endif
+ *shift = 16;
+ break;
+ case ASIOSTInt32MSB18:
+ /* src: paInt32, 14 bit shift, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = ShiftRightSwap32;
+ #else
+ *converter = ShiftRight32;
+ #endif
+ *shift = 14;
+ break;
+ case ASIOSTInt32MSB20:
+ /* src: paInt32, 12 bit shift, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = ShiftRightSwap32;
+ #else
+ *converter = ShiftRight32;
+ #endif
+ *shift = 12;
+ break;
+ case ASIOSTInt32MSB24:
+ /* src: paInt32, 8 bit shift, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = ShiftRightSwap32;
+ #else
+ *converter = ShiftRight32;
+ #endif
+ *shift = 8;
+ break;
+ case ASIOSTInt32LSB16:
+ /* src: paInt32, 16 bit shift, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = ShiftRightSwap32;
+ #else
+ *converter = ShiftRight32;
+ #endif
+ *shift = 16;
+ break;
+ case ASIOSTInt32LSB18:
+ /* src: paInt32, 14 bit shift, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = ShiftRightSwap32;
+ #else
+ *converter = ShiftRight32;
+ #endif
+ *shift = 14;
+ break;
+ case ASIOSTInt32LSB20:
+ /* src: paInt32, 12 bit shift, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = ShiftRightSwap32;
+ #else
+ *converter = ShiftRight32;
+ #endif
+ *shift = 12;
+ break;
+ case ASIOSTInt32LSB24:
+ /* src: paInt32, 8 bit shift, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = ShiftRightSwap32;
+ #else
+ *converter = ShiftRight32;
+ #endif
+ *shift = 8;
+ break;
+ case ASIOSTInt24MSB:
+ /* src: paInt24, no conversion necessary, possible byte swap */
+ #ifdef PA_LSB_IS_NATIVE_
+ *converter = Swap24;
+ #endif
+ break;
+ case ASIOSTInt24LSB:
+ /* src: paInt24, no conversion necessary, possible byte swap */
+ #ifdef PA_MSB_IS_NATIVE_
+ *converter = Swap24;
+ #endif
+ break;
+ }
+}
+
+
+typedef struct PaAsioDeviceInfo
+{
+ PaDeviceInfo commonDeviceInfo;
+ long minBufferSize;
+ long maxBufferSize;
+ long preferredBufferSize;
+ long bufferGranularity;
+}
+PaAsioDeviceInfo;
+
+
+PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
+ long *minLatency, long *maxLatency, long *preferredLatency, long *granularity )
+{
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi;
+ PaDeviceIndex hostApiDevice;
+
+ result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
+
+ if( result == paNoError )
+ {
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
+
+ if( result == paNoError )
+ {
+ PaAsioDeviceInfo *asioDeviceInfo =
+ (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
+
+ *minLatency = asioDeviceInfo->minBufferSize;
+ *maxLatency = asioDeviceInfo->maxBufferSize;
+ *preferredLatency = asioDeviceInfo->preferredBufferSize;
+ *granularity = asioDeviceInfo->bufferGranularity;
+ }
+ }
+
+ return result;
+}
+
+
+
+/*
+ load the asio driver named by <driverName> and return statistics about
+ the driver in info. If no error occurred, the driver will remain open
+ and must be closed by the called by calling ASIOExit() - if an error
+ is returned the driver will already be closed.
+*/
+static PaError LoadAsioDriver( const char *driverName,
+ PaAsioDriverInfo *driverInfo, void *systemSpecific )
+{
+ PaError result = paNoError;
+ ASIOError asioError;
+ int asioIsInitialized = 0;
+
+ if( !loadAsioDriver( const_cast<char*>(driverName) ) )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
+ goto error;
+ }
+
+ memset( &driverInfo->asioDriverInfo, 0, sizeof(ASIODriverInfo) );
+ driverInfo->asioDriverInfo.asioVersion = 2;
+ driverInfo->asioDriverInfo.sysRef = systemSpecific;
+ if( (asioError = ASIOInit( &driverInfo->asioDriverInfo )) != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ goto error;
+ }
+ else
+ {
+ asioIsInitialized = 1;
+ }
+
+ if( (asioError = ASIOGetChannels(&driverInfo->inputChannelCount,
+ &driverInfo->outputChannelCount)) != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ goto error;
+ }
+
+ if( (asioError = ASIOGetBufferSize(&driverInfo->bufferMinSize,
+ &driverInfo->bufferMaxSize, &driverInfo->bufferPreferredSize,
+ &driverInfo->bufferGranularity)) != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ goto error;
+ }
+
+ if( ASIOOutputReady() == ASE_OK )
+ driverInfo->postOutput = true;
+ else
+ driverInfo->postOutput = false;
+
+ return result;
+
+error:
+ if( asioIsInitialized )
+ ASIOExit();
+
+ return result;
+}
+
+
+#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ 13 /* must be the same number of elements as in the array below */
+static ASIOSampleRate defaultSampleRateSearchOrder_[]
+ = {44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0,
+ 192000.0, 16000.0, 12000.0, 11025.0, 96000.0, 8000.0 };
+
+
+PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i, driverCount;
+ PaAsioHostApiRepresentation *asioHostApi;
+ PaAsioDeviceInfo *deviceInfoArray;
+ char **names;
+ PaAsioDriverInfo paAsioDriverInfo;
+ ASIOChannelInfo asioChannelInfo;
+
+ asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaAsioHostApiRepresentation) );
+ if( !asioHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ asioHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !asioHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ asioHostApi->systemSpecific = 0;
+ asioHostApi->openAsioDeviceIndex = paNoDevice;
+
+ *hostApi = &asioHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+
+ (*hostApi)->info.type = paASIO;
+ (*hostApi)->info.name = "ASIO";
+ (*hostApi)->info.deviceCount = 0;
+
+ #ifdef WINDOWS
+ /* use desktop window as system specific ptr */
+ asioHostApi->systemSpecific = GetDesktopWindow();
+ CoInitialize(NULL);
+ #endif
+
+ /* MUST BE CHECKED : to force fragments loading on Mac */
+ loadAsioDriver( "dummy" );
+
+ /* driverCount is the number of installed drivers - not necessarily
+ the number of installed physical devices. */
+ #if MAC
+ driverCount = asioDrivers->getNumFragments();
+ #elif WINDOWS
+ driverCount = asioDrivers->asioGetNumDev();
+ #endif
+
+ if( driverCount > 0 )
+ {
+ names = GetAsioDriverNames( asioHostApi->allocations, driverCount );
+ if( !names )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+
+ /* allocate enough space for all drivers, even if some aren't installed */
+
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ asioHostApi->allocations, sizeof(PaDeviceInfo*) * driverCount );
+ if( !(*hostApi)->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory(
+ asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i=0; i < driverCount; ++i )
+ {
+ /* Attempt to load the asio driver... */
+ if( LoadAsioDriver( names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError )
+ {
+ PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
+ PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo;
+
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+
+ deviceInfo->name = names[i];
+ PA_DEBUG(("PaAsio_Initialize: drv:%d name = %s\n", i,deviceInfo->name));
+
+ deviceInfo->maxInputChannels = paAsioDriverInfo.inputChannelCount;
+ deviceInfo->maxOutputChannels = paAsioDriverInfo.outputChannelCount;
+
+ PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels = %d\n", i, paAsioDriverInfo.inputChannelCount));
+ PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels = %d\n", i, paAsioDriverInfo.outputChannelCount));
+
+ deviceInfo->defaultSampleRate = 0.;
+ bool foundDefaultSampleRate = false;
+ for( int j=0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j )
+ {
+ ASIOError asioError = ASIOCanSampleRate( defaultSampleRateSearchOrder_[j] );
+ if( asioError != ASE_NoClock && asioError != ASE_NotPresent )
+ {
+ deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j];
+ foundDefaultSampleRate = true;
+ break;
+ }
+ }
+
+ PA_DEBUG(("PaAsio_Initialize: drv:%d defaultSampleRate = %f\n", i, deviceInfo->defaultSampleRate));
+
+ if( foundDefaultSampleRate ){
+
+ /* calculate default latency values from bufferPreferredSize
+ for default low latency, and bufferPreferredSize * 3
+ for default high latency.
+ use the default sample rate to convert from samples to
+ seconds. Without knowing what sample rate the user will
+ use this is the best we can do.
+ */
+
+ double defaultLowLatency =
+ paAsioDriverInfo.bufferPreferredSize / deviceInfo->defaultSampleRate;
+
+ deviceInfo->defaultLowInputLatency = defaultLowLatency;
+ deviceInfo->defaultLowOutputLatency = defaultLowLatency;
+
+ long defaultHighLatencyBufferSize =
+ paAsioDriverInfo.bufferPreferredSize * 3;
+
+ if( defaultHighLatencyBufferSize > paAsioDriverInfo.bufferMaxSize )
+ defaultHighLatencyBufferSize = paAsioDriverInfo.bufferMaxSize;
+
+ double defaultHighLatency =
+ defaultHighLatencyBufferSize / deviceInfo->defaultSampleRate;
+
+ if( defaultHighLatency < defaultLowLatency )
+ defaultHighLatency = defaultLowLatency; /* just incase the driver returns something strange */
+
+ deviceInfo->defaultHighInputLatency = defaultHighLatency;
+ deviceInfo->defaultHighOutputLatency = defaultHighLatency;
+
+ }else{
+
+ deviceInfo->defaultLowInputLatency = 0.;
+ deviceInfo->defaultLowOutputLatency = 0.;
+ deviceInfo->defaultHighInputLatency = 0.;
+ deviceInfo->defaultHighOutputLatency = 0.;
+ }
+
+ PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowInputLatency = %f\n", i, deviceInfo->defaultLowInputLatency));
+ PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowOutputLatency = %f\n", i, deviceInfo->defaultLowOutputLatency));
+ PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighInputLatency = %f\n", i, deviceInfo->defaultHighInputLatency));
+ PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighOutputLatency = %f\n", i, deviceInfo->defaultHighOutputLatency));
+
+ asioDeviceInfo->minBufferSize = paAsioDriverInfo.bufferMinSize;
+ asioDeviceInfo->maxBufferSize = paAsioDriverInfo.bufferMaxSize;
+ asioDeviceInfo->preferredBufferSize = paAsioDriverInfo.bufferPreferredSize;
+ asioDeviceInfo->bufferGranularity = paAsioDriverInfo.bufferGranularity;
+
+
+ /* We assume that all channels have the same SampleType, so check the first, FIXME, probably shouldn't assume that */
+ asioChannelInfo.channel = 0;
+ asioChannelInfo.isInput = 1;
+ ASIOGetChannelInfo( &asioChannelInfo ); /* FIXME, check return code */
+
+
+ /* unload the driver */
+ ASIOExit();
+
+ (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
+ ++(*hostApi)->info.deviceCount;
+ }
+ }
+ }
+
+ if( (*hostApi)->info.deviceCount > 0 )
+ {
+ (*hostApi)->info.defaultInputDevice = 0;
+ (*hostApi)->info.defaultOutputDevice = 0;
+ }
+ else
+ {
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+ }
+
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &asioHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ return result;
+
+error:
+ if( asioHostApi )
+ {
+ if( asioHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( asioHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( asioHostApi );
+ }
+ return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+
+ /*
+ IMPLEMENT ME:
+ - clean up any resources not handled by the allocation group
+ */
+
+ if( asioHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( asioHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
+ }
+
+ PaUtil_FreeMemory( asioHostApi );
+}
+
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ PaError result = paNoError;
+ PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+ PaAsioDriverInfo *driverInfo = &asioHostApi->openAsioDriverInfo;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaDeviceIndex asioDeviceIndex;
+ ASIOError asioError;
+
+ if( inputParameters && outputParameters )
+ {
+ /* full duplex ASIO stream must use the same device for input and output */
+
+ if( inputParameters->device != outputParameters->device )
+ return paBadIODeviceCombination;
+ }
+
+ 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;
+
+ asioDeviceIndex = inputParameters->device;
+
+ /* 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;
+
+ asioDeviceIndex = outputParameters->device;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+
+
+ /* if an ASIO device is open we can only get format information for the currently open device */
+
+ if( asioHostApi->openAsioDeviceIndex != paNoDevice
+ && asioHostApi->openAsioDeviceIndex != asioDeviceIndex )
+ {
+ return paDeviceUnavailable;
+ }
+
+
+ /* NOTE: we load the driver and use its current settings
+ rather than the ones in our device info structure which may be stale */
+
+ /* open the device if it's not already open */
+ if( asioHostApi->openAsioDeviceIndex == paNoDevice )
+ {
+ result = LoadAsioDriver( asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
+ driverInfo, asioHostApi->systemSpecific );
+ if( result != paNoError )
+ return result;
+ }
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > 0 )
+ {
+ if( inputChannelCount > driverInfo->inputChannelCount )
+ {
+ result = paInvalidChannelCount;
+ goto done;
+ }
+ }
+
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount )
+ {
+ if( outputChannelCount > driverInfo->outputChannelCount )
+ {
+ result = paInvalidChannelCount;
+ goto done;
+ }
+ }
+
+ /* query for sample rate support */
+ asioError = ASIOCanSampleRate( sampleRate );
+ if( asioError == ASE_NoClock || asioError == ASE_NotPresent )
+ {
+ result = paInvalidSampleRate;
+ goto done;
+ }
+
+done:
+ /* close the device if it wasn't already open */
+ if( asioHostApi->openAsioDeviceIndex == paNoDevice )
+ {
+ ASIOExit(); /* not sure if we should check for errors here */
+ }
+
+ if( result == paNoError )
+ return paFormatIsSupported;
+ else
+ return result;
+}
+
+
+
+/* PaAsioStream - a stream data structure specifically for this implementation */
+
+typedef struct PaAsioStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ PaAsioHostApiRepresentation *asioHostApi;
+ unsigned long framesPerHostCallback;
+
+ /* ASIO driver info - these may not be needed for the life of the stream,
+ but store them here until we work out how format conversion is going
+ to work. */
+
+ ASIOBufferInfo *asioBufferInfos;
+ ASIOChannelInfo *asioChannelInfos;
+ long inputLatency, outputLatency; // actual latencies returned by asio
+
+ long inputChannelCount, outputChannelCount;
+ bool postOutput;
+
+ void **bufferPtrs; /* this is carved up for inputBufferPtrs and outputBufferPtrs */
+ void **inputBufferPtrs[2];
+ void **outputBufferPtrs[2];
+
+ PaAsioBufferConverter *inputBufferConverter;
+ long inputShift;
+ PaAsioBufferConverter *outputBufferConverter;
+ long outputShift;
+
+ volatile bool stopProcessing;
+ int stopPlayoutCount;
+ HANDLE completedBuffersPlayedEvent;
+
+ bool streamFinishedCallbackCalled;
+ volatile int isActive;
+ volatile bool zeroOutput; /* all future calls to the callback will output silence */
+
+ volatile long reenterCount;
+ volatile long reenterError;
+
+ PaStreamCallbackFlags callbackFlags;
+}
+PaAsioStream;
+
+static PaAsioStream *theAsioStream = 0; /* due to ASIO sdk limitations there can be only one stream */
+
+
+static void ZeroOutputBuffers( PaAsioStream *stream, long index )
+{
+ int i;
+
+ for( i=0; i < stream->outputChannelCount; ++i )
+ {
+ void *buffer = stream->asioBufferInfos[ i + stream->inputChannelCount ].buffers[index];
+
+ int bytesPerSample = BytesPerAsioSample( stream->asioChannelInfos[ i + stream->inputChannelCount ].type );
+
+ memset( buffer, 0, stream->framesPerHostCallback * bytesPerSample );
+ }
+}
+
+
+static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames,
+ PaAsioDriverInfo *driverInfo )
+{
+ unsigned long result;
+
+ if( suggestedLatencyFrames == 0 )
+ {
+ result = driverInfo->bufferPreferredSize;
+ }
+ else{
+ if( suggestedLatencyFrames <= (unsigned long)driverInfo->bufferMinSize )
+ {
+ result = driverInfo->bufferMinSize;
+ }
+ else if( suggestedLatencyFrames >= (unsigned long)driverInfo->bufferMaxSize )
+ {
+ result = driverInfo->bufferMaxSize;
+ }
+ else
+ {
+ if( driverInfo->bufferGranularity == -1 )
+ {
+ /* power-of-two */
+ result = 2;
+
+ while( result < suggestedLatencyFrames )
+ result *= result;
+
+ if( result < (unsigned long)driverInfo->bufferMinSize )
+ result = driverInfo->bufferMinSize;
+
+ if( result > (unsigned long)driverInfo->bufferMaxSize )
+ result = driverInfo->bufferMaxSize;
+ }
+ else if( driverInfo->bufferGranularity == 0 )
+ {
+ /* the documentation states that bufferGranularity should be
+ zero when bufferMinSize, bufferMaxSize and
+ bufferPreferredSize are the same. We assume that is the case.
+ */
+
+ result = driverInfo->bufferPreferredSize;
+ }
+ else
+ {
+ /* modulo granularity */
+
+ unsigned long remainder =
+ suggestedLatencyFrames % driverInfo->bufferGranularity;
+
+ if( remainder == 0 )
+ {
+ result = suggestedLatencyFrames;
+ }
+ else
+ {
+ result = suggestedLatencyFrames
+ + (driverInfo->bufferGranularity - remainder);
+
+ if( result > (unsigned long)driverInfo->bufferMaxSize )
+ result = driverInfo->bufferMaxSize;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+
+/* 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;
+ PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+ PaAsioStream *stream = 0;
+ unsigned long framesPerHostBuffer;
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+ unsigned long suggestedInputLatencyFrames;
+ unsigned long suggestedOutputLatencyFrames;
+ PaDeviceIndex asioDeviceIndex;
+ ASIOError asioError;
+ int asioIsInitialized = 0;
+ int asioBuffersCreated = 0;
+ int completedBuffersPlayedEventInited = 0;
+ int i;
+ PaAsioDriverInfo *driverInfo;
+
+ /* unless we move to using lower level ASIO calls, we can only have
+ one device open at a time */
+ if( asioHostApi->openAsioDeviceIndex != paNoDevice )
+ return paDeviceUnavailable;
+
+ if( inputParameters && outputParameters )
+ {
+ /* full duplex ASIO stream must use the same device for input and output */
+
+ if( inputParameters->device != outputParameters->device )
+ return paBadIODeviceCombination;
+ }
+
+ if( inputParameters )
+ {
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+ suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
+
+ asioDeviceIndex = inputParameters->device;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* validate hostApiSpecificStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ inputChannelCount = 0;
+ inputSampleFormat = 0;
+ suggestedInputLatencyFrames = 0;
+ }
+
+ if( outputParameters )
+ {
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+ suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
+
+ asioDeviceIndex = outputParameters->device;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* validate hostApiSpecificStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+ else
+ {
+ outputChannelCount = 0;
+ outputSampleFormat = 0;
+ suggestedOutputLatencyFrames = 0;
+ }
+
+ driverInfo = &asioHostApi->openAsioDriverInfo;
+
+ /* NOTE: we load the driver and use its current settings
+ rather than the ones in our device info structure which may be stale */
+
+ result = LoadAsioDriver( asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
+ driverInfo, asioHostApi->systemSpecific );
+ if( result == paNoError )
+ asioIsInitialized = 1;
+ else
+ goto error;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > 0 )
+ {
+ if( inputChannelCount > driverInfo->inputChannelCount )
+ {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+ }
+
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount )
+ {
+ if( outputChannelCount > driverInfo->outputChannelCount )
+ {
+ result = paInvalidChannelCount;
+ goto error;
+ }
+ }
+
+ /* Set sample rate */
+ if( ASIOSetSampleRate( sampleRate ) != ASE_OK )
+ {
+ result = paInvalidSampleRate;
+ goto error;
+ }
+
+ /*
+ IMPLEMENT ME:
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported
+ */
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+
+ stream = (PaAsioStream*)PaUtil_AllocateMemory( sizeof(PaAsioStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ stream->completedBuffersPlayedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+ if( stream->completedBuffersPlayedEvent == NULL )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ goto error;
+ }
+ completedBuffersPlayedEventInited = 1;
+
+
+ stream->asioBufferInfos = 0; /* for deallocation in error */
+ stream->asioChannelInfos = 0; /* for deallocation in error */
+ stream->bufferPtrs = 0; /* for deallocation in error */
+
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &asioHostApi->callbackStreamInterface, streamCallback, userData );
+ }
+ else
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &asioHostApi->blockingStreamInterface, streamCallback, userData );
+ }
+
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+
+ stream->asioBufferInfos = (ASIOBufferInfo*)PaUtil_AllocateMemory(
+ sizeof(ASIOBufferInfo) * (inputChannelCount + outputChannelCount) );
+ if( !stream->asioBufferInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+
+ for( i=0; i < inputChannelCount; ++i )
+ {
+ ASIOBufferInfo *info = &stream->asioBufferInfos[i];
+
+ info->isInput = ASIOTrue;
+ info->channelNum = i;
+ info->buffers[0] = info->buffers[1] = 0;
+ }
+
+ for( i=0; i < outputChannelCount; ++i ){
+ ASIOBufferInfo *info = &stream->asioBufferInfos[inputChannelCount+i];
+
+ info->isInput = ASIOFalse;
+ info->channelNum = i;
+ info->buffers[0] = info->buffers[1] = 0;
+ }
+
+
+ framesPerHostBuffer = SelectHostBufferSize(
+ (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
+ ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames),
+ driverInfo );
+
+ asioError = ASIOCreateBuffers( stream->asioBufferInfos,
+ inputChannelCount+outputChannelCount,
+ framesPerHostBuffer, &asioCallbacks_ );
+
+ if( asioError != ASE_OK
+ && framesPerHostBuffer != (unsigned long)driverInfo->bufferPreferredSize )
+ {
+ /*
+ Some buggy drivers (like the Hoontech DSP24) give incorrect
+ [min, preferred, max] values They should work with the preferred size
+ value, thus if Pa_ASIO_CreateBuffers fails with the hostBufferSize
+ computed in SelectHostBufferSize, we try again with the preferred size.
+ */
+
+ framesPerHostBuffer = driverInfo->bufferPreferredSize;
+
+ ASIOError asioError2 = ASIOCreateBuffers( stream->asioBufferInfos,
+ inputChannelCount+outputChannelCount,
+ framesPerHostBuffer, &asioCallbacks_ );
+ if( asioError2 == ASE_OK )
+ asioError = ASE_OK;
+ }
+
+ if( asioError != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ goto error;
+ }
+
+ asioBuffersCreated = 1;
+
+ stream->asioChannelInfos = (ASIOChannelInfo*)PaUtil_AllocateMemory(
+ sizeof(ASIOChannelInfo) * (inputChannelCount + outputChannelCount) );
+ if( !stream->asioChannelInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i=0; i < inputChannelCount + outputChannelCount; ++i )
+ {
+ stream->asioChannelInfos[i].channel = stream->asioBufferInfos[i].channelNum;
+ stream->asioChannelInfos[i].isInput = stream->asioBufferInfos[i].isInput;
+ asioError = ASIOGetChannelInfo( &stream->asioChannelInfos[i] );
+ if( asioError != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ goto error;
+ }
+ }
+
+ stream->bufferPtrs = (void**)PaUtil_AllocateMemory(
+ 2 * sizeof(void*) * (inputChannelCount + outputChannelCount) );
+ if( !stream->bufferPtrs )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if( inputChannelCount > 0 )
+ {
+ stream->inputBufferPtrs[0] = stream-> bufferPtrs;
+ stream->inputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount];
+
+ for( i=0; i<inputChannelCount; ++i )
+ {
+ stream->inputBufferPtrs[0][i] = stream->asioBufferInfos[i].buffers[0];
+ stream->inputBufferPtrs[1][i] = stream->asioBufferInfos[i].buffers[1];
+ }
+ }
+ else
+ {
+ stream->inputBufferPtrs[0] = 0;
+ stream->inputBufferPtrs[1] = 0;
+ }
+
+ if( outputChannelCount > 0 )
+ {
+ stream->outputBufferPtrs[0] = &stream->bufferPtrs[inputChannelCount*2];
+ stream->outputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount*2 + outputChannelCount];
+
+ for( i=0; i<outputChannelCount; ++i )
+ {
+ stream->outputBufferPtrs[0][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[0];
+ stream->outputBufferPtrs[1][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[1];
+ }
+ }
+ else
+ {
+ stream->outputBufferPtrs[0] = 0;
+ stream->outputBufferPtrs[1] = 0;
+ }
+
+ if( inputChannelCount > 0 )
+ {
+ /* FIXME: assume all channels use the same type for now */
+ ASIOSampleType inputType = stream->asioChannelInfos[0].type;
+
+ hostInputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( inputType );
+
+ SelectAsioToPaConverter( inputType, &stream->inputBufferConverter, &stream->inputShift );
+ }
+ else
+ {
+ hostInputSampleFormat = 0;
+ stream->inputBufferConverter = 0;
+ }
+
+ if( outputChannelCount > 0 )
+ {
+ /* FIXME: assume all channels use the same type for now */
+ ASIOSampleType outputType = stream->asioChannelInfos[inputChannelCount].type;
+
+ hostOutputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( outputType );
+
+ SelectPaToAsioConverter( outputType, &stream->outputBufferConverter, &stream->outputShift );
+ }
+ else
+ {
+ hostOutputSampleFormat = 0;
+ stream->outputBufferConverter = 0;
+ }
+
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerHostBuffer, paUtilFixedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError )
+ goto error;
+
+
+ ASIOGetLatencies( &stream->inputLatency, &stream->outputLatency );
+
+ stream->streamRepresentation.streamInfo.inputLatency =
+ (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
+ + stream->inputLatency) / sampleRate; // seconds
+ stream->streamRepresentation.streamInfo.outputLatency =
+ (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
+ + stream->outputLatency) / sampleRate; // seconds
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ // the code below prints the ASIO latency which doesn't include the
+ // buffer processor latency.
+ PA_DEBUG(("PaAsio : ASIO InputLatency = %ld latency = %ld msec \n",
+ stream->inputLatency,
+ (long)((stream->inputLatency*1000)/ sampleRate)));
+ PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld latency = %ld msec \n",
+ stream->outputLatency,
+ (long)((stream->outputLatency*1000)/ sampleRate)));
+
+ stream->asioHostApi = asioHostApi;
+ stream->framesPerHostCallback = framesPerHostBuffer;
+
+ stream->inputChannelCount = inputChannelCount;
+ stream->outputChannelCount = outputChannelCount;
+ stream->postOutput = driverInfo->postOutput;
+ stream->isActive = 0;
+
+ asioHostApi->openAsioDeviceIndex = asioDeviceIndex;
+
+ *s = (PaStream*)stream;
+
+ return result;
+
+error:
+ if( stream )
+ {
+ if( completedBuffersPlayedEventInited )
+ CloseHandle( stream->completedBuffersPlayedEvent );
+
+ if( stream->asioBufferInfos )
+ PaUtil_FreeMemory( stream->asioBufferInfos );
+
+ if( stream->asioChannelInfos )
+ PaUtil_FreeMemory( stream->asioChannelInfos );
+
+ if( stream->bufferPtrs )
+ PaUtil_FreeMemory( stream->bufferPtrs );
+
+ PaUtil_FreeMemory( stream );
+ }
+
+ if( asioBuffersCreated )
+ ASIODisposeBuffers();
+
+ if( asioIsInitialized )
+ ASIOExit();
+
+ return result;
+}
+
+
+/*
+ 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;
+ PaAsioStream *stream = (PaAsioStream*)s;
+
+ /*
+ IMPLEMENT ME:
+ - additional stream closing + cleanup
+ */
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+ stream->asioHostApi->openAsioDeviceIndex = paNoDevice;
+
+ CloseHandle( stream->completedBuffersPlayedEvent );
+
+ PaUtil_FreeMemory( stream->asioBufferInfos );
+ PaUtil_FreeMemory( stream->asioChannelInfos );
+ PaUtil_FreeMemory( stream->bufferPtrs );
+ PaUtil_FreeMemory( stream );
+
+ ASIODisposeBuffers();
+ ASIOExit();
+
+ return result;
+}
+
+
+static void bufferSwitch(long index, ASIOBool directProcess)
+{
+//TAKEN FROM THE ASIO SDK
+
+ // the actual processing callback.
+ // Beware that this is normally in a seperate thread, hence be sure that
+ // you take care about thread synchronization. This is omitted here for
+ // simplicity.
+
+ // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs
+ // to be created though it will only set the timeInfo.samplePosition and
+ // timeInfo.systemTime fields and the according flags
+
+ ASIOTime timeInfo;
+ memset( &timeInfo, 0, sizeof (timeInfo) );
+
+ // get the time stamp of the buffer, not necessary if no
+ // synchronization to other media is required
+ if( ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
+ timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
+
+ // Call the real callback
+ bufferSwitchTimeInfo( &timeInfo, index, directProcess );
+}
+
+
+// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
+#if NATIVE_INT64
+ #define ASIO64toDouble(a) (a)
+#else
+ const double twoRaisedTo32 = 4294967296.;
+ #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
+#endif
+
+static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool directProcess )
+{
+ // the actual processing callback.
+ // Beware that this is normally in a seperate thread, hence be sure that
+ // you take care about thread synchronization.
+
+
+ /* The SDK says the following about the directProcess flag:
+ suggests to the host whether it should immediately start processing
+ (directProcess == ASIOTrue), or whether its process should be deferred
+ because the call comes from a very low level (for instance, a high level
+ priority interrupt), and direct processing would cause timing instabilities for
+ the rest of the system. If in doubt, directProcess should be set to ASIOFalse.
+
+ We just ignore directProcess. This could cause incompatibilities with
+ drivers which really don't want the audio processing to occur in this
+ callback, but none have been identified yet.
+ */
+
+ (void) directProcess; /* suppress unused parameter warning */
+
+#if 0
+ // store the timeInfo for later use
+ asioDriverInfo.tInfo = *timeInfo;
+
+ // get the time stamp of the buffer, not necessary if no
+ // synchronization to other media is required
+
+ if (timeInfo->timeInfo.flags & kSystemTimeValid)
+ asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
+ else
+ asioDriverInfo.nanoSeconds = 0;
+
+ if (timeInfo->timeInfo.flags & kSamplePositionValid)
+ asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
+ else
+ asioDriverInfo.samples = 0;
+
+ if (timeInfo->timeCode.flags & kTcValid)
+ asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
+ else
+ asioDriverInfo.tcSamples = 0;
+
+ // get the system reference time
+ asioDriverInfo.sysRefTime = get_sys_reference_time();
+#endif
+
+#if 0
+ // a few debug messages for the Windows device driver developer
+ // tells you the time when driver got its interrupt and the delay until the app receives
+ // the event notification.
+ static double last_samples = 0;
+ char tmp[128];
+ sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
+ OutputDebugString (tmp);
+ last_samples = asioDriverInfo.samples;
+#endif
+
+
+ if( !theAsioStream )
+ return 0L;
+
+ // Keep sample position
+ // FIXME: asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo;
+
+
+ // protect against reentrancy
+ if( PaAsio_AtomicIncrement(&theAsioStream->reenterCount) )
+ {
+ theAsioStream->reenterError++;
+ //DBUG(("bufferSwitchTimeInfo : reentrancy detection = %d\n", asioDriverInfo.reenterError));
+ return 0L;
+ }
+
+ int buffersDone = 0;
+
+ do
+ {
+ if( buffersDone > 0 )
+ {
+ // this is a reentered buffer, we missed processing it on time
+ // set the input overflow and output underflow flags as appropriate
+
+ if( theAsioStream->inputChannelCount > 0 )
+ theAsioStream->callbackFlags |= paInputOverflow;
+
+ if( theAsioStream->outputChannelCount > 0 )
+ theAsioStream->callbackFlags |= paOutputUnderflow;
+ }
+ else
+ {
+ if( theAsioStream->zeroOutput )
+ {
+ ZeroOutputBuffers( theAsioStream, index );
+
+ // Finally if the driver supports the ASIOOutputReady() optimization,
+ // do it here, all data are in place
+ if( theAsioStream->postOutput )
+ ASIOOutputReady();
+
+ if( theAsioStream->stopProcessing )
+ {
+ if( theAsioStream->stopPlayoutCount < 2 )
+ {
+ ++theAsioStream->stopPlayoutCount;
+ if( theAsioStream->stopPlayoutCount == 2 )
+ {
+ theAsioStream->isActive = 0;
+ if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
+ theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
+ theAsioStream->streamFinishedCallbackCalled = true;
+ SetEvent( theAsioStream->completedBuffersPlayedEvent );
+ }
+ }
+ }
+ }
+ else
+ {
+
+#if 0
+// test code to try to detect slip conditions... these may work on some systems
+// but neither of them work on the RME Digi96
+
+// check that sample delta matches buffer size (otherwise we must have skipped
+// a buffer.
+static double last_samples = -512;
+double samples;
+//if( timeInfo->timeCode.flags & kTcValid )
+// samples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
+//else
+ samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
+int delta = samples - last_samples;
+//printf( "%d\n", delta);
+last_samples = samples;
+
+if( delta > theAsioStream->framesPerHostCallback )
+{
+ if( theAsioStream->inputChannelCount > 0 )
+ theAsioStream->callbackFlags |= paInputOverflow;
+
+ if( theAsioStream->outputChannelCount > 0 )
+ theAsioStream->callbackFlags |= paOutputUnderflow;
+}
+
+// check that the buffer index is not the previous index (which would indicate
+// that a buffer was skipped.
+static int previousIndex = 1;
+if( index == previousIndex )
+{
+ if( theAsioStream->inputChannelCount > 0 )
+ theAsioStream->callbackFlags |= paInputOverflow;
+
+ if( theAsioStream->outputChannelCount > 0 )
+ theAsioStream->callbackFlags |= paOutputUnderflow;
+}
+previousIndex = index;
+#endif
+
+ int i;
+
+ PaUtil_BeginCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer );
+
+ PaStreamCallbackTimeInfo paTimeInfo;
+
+ // asio systemTime is supposed to be measured according to the same
+ // clock as timeGetTime
+ paTimeInfo.currentTime = (ASIO64toDouble( timeInfo->timeInfo.systemTime ) * .000000001);
+ paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency;
+ paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency;
+
+#if 1
+// detect underflows by checking inter-callback time > 2 buffer period
+static double previousTime = -1;
+if( previousTime > 0 ){
+
+ double delta = paTimeInfo.currentTime - previousTime;
+
+ if( delta >= 2. * (theAsioStream->framesPerHostCallback / theAsioStream->streamRepresentation.streamInfo.sampleRate) ){
+ if( theAsioStream->inputChannelCount > 0 )
+ theAsioStream->callbackFlags |= paInputOverflow;
+
+ if( theAsioStream->outputChannelCount > 0 )
+ theAsioStream->callbackFlags |= paOutputUnderflow;
+ }
+}
+previousTime = paTimeInfo.currentTime;
+#endif
+
+ // note that the above input and output times do not need to be
+ // adjusted for the latency of the buffer processor -- the buffer
+ // processor handles that.
+
+ if( theAsioStream->inputBufferConverter )
+ {
+ for( i=0; i<theAsioStream->inputChannelCount; i++ )
+ {
+ theAsioStream->inputBufferConverter( theAsioStream->inputBufferPtrs[index][i],
+ theAsioStream->inputShift, theAsioStream->framesPerHostCallback );
+ }
+ }
+
+ PaUtil_BeginBufferProcessing( &theAsioStream->bufferProcessor, &paTimeInfo, theAsioStream->callbackFlags );
+
+ /* reset status flags once they've been passed to the callback */
+ theAsioStream->callbackFlags = 0;
+
+ PaUtil_SetInputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
+ for( i=0; i<theAsioStream->inputChannelCount; ++i )
+ PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] );
+
+ PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
+ for( i=0; i<theAsioStream->outputChannelCount; ++i )
+ PaUtil_SetNonInterleavedOutputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->outputBufferPtrs[index][i] );
+
+ int callbackResult;
+ if( theAsioStream->stopProcessing )
+ callbackResult = paComplete;
+ else
+ callbackResult = paContinue;
+ unsigned long framesProcessed = PaUtil_EndBufferProcessing( &theAsioStream->bufferProcessor, &callbackResult );
+
+ if( theAsioStream->outputBufferConverter )
+ {
+ for( i=0; i<theAsioStream->outputChannelCount; i++ )
+ {
+ theAsioStream->outputBufferConverter( theAsioStream->outputBufferPtrs[index][i],
+ theAsioStream->outputShift, theAsioStream->framesPerHostCallback );
+ }
+ }
+
+ PaUtil_EndCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer, framesProcessed );
+
+ // Finally if the driver supports the ASIOOutputReady() optimization,
+ // do it here, all data are in place
+ if( theAsioStream->postOutput )
+ ASIOOutputReady();
+
+ if( callbackResult == paContinue )
+ {
+ /* nothing special to do */
+ }
+ else if( callbackResult == paAbort )
+ {
+ /* finish playback immediately */
+ theAsioStream->isActive = 0;
+ if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
+ theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
+ theAsioStream->streamFinishedCallbackCalled = true;
+ SetEvent( theAsioStream->completedBuffersPlayedEvent );
+ theAsioStream->zeroOutput = true;
+ }
+ else /* paComplete or other non-zero value indicating complete */
+ {
+ /* Finish playback once currently queued audio has completed. */
+ theAsioStream->stopProcessing = true;
+
+ if( PaUtil_IsBufferProcessorOuputEmpty( &theAsioStream->bufferProcessor ) )
+ {
+ theAsioStream->zeroOutput = true;
+ theAsioStream->stopPlayoutCount = 0;
+ }
+ }
+ }
+ }
+
+ ++buffersDone;
+ }while( PaAsio_AtomicDecrement(&theAsioStream->reenterCount) >= 0 );
+
+ return 0L;
+}
+
+
+static void sampleRateChanged(ASIOSampleRate sRate)
+{
+ // TAKEN FROM THE ASIO SDK
+ // do whatever you need to do if the sample rate changed
+ // usually this only happens during external sync.
+ // Audio processing is not stopped by the driver, actual sample rate
+ // might not have even changed, maybe only the sample rate status of an
+ // AES/EBU or S/PDIF digital input at the audio device.
+ // You might have to update time/sample related conversion routines, etc.
+
+ (void) sRate; /* unused parameter */
+}
+
+static long asioMessages(long selector, long value, void* message, double* opt)
+{
+// TAKEN FROM THE ASIO SDK
+ // currently the parameters "value", "message" and "opt" are not used.
+ long ret = 0;
+
+ (void) message; /* unused parameters */
+ (void) opt;
+
+ switch(selector)
+ {
+ case kAsioSelectorSupported:
+ if(value == kAsioResetRequest
+ || value == kAsioEngineVersion
+ || value == kAsioResyncRequest
+ || value == kAsioLatenciesChanged
+ // the following three were added for ASIO 2.0, you don't necessarily have to support them
+ || value == kAsioSupportsTimeInfo
+ || value == kAsioSupportsTimeCode
+ || value == kAsioSupportsInputMonitor)
+ ret = 1L;
+ break;
+
+ case kAsioBufferSizeChange:
+ //printf("kAsioBufferSizeChange \n");
+ break;
+
+ case kAsioResetRequest:
+ // defer the task and perform the reset of the driver during the next "safe" situation
+ // You cannot reset the driver right now, as this code is called from the driver.
+ // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
+ // Afterwards you initialize the driver again.
+
+ /*FIXME: commented the next line out */
+ //asioDriverInfo.stopped; // In this sample the processing will just stop
+ ret = 1L;
+ break;
+
+ case kAsioResyncRequest:
+ // This informs the application, that the driver encountered some non fatal data loss.
+ // It is used for synchronization purposes of different media.
+ // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
+ // Windows Multimedia system, which could loose data because the Mutex was hold too long
+ // by another thread.
+ // However a driver can issue it in other situations, too.
+ ret = 1L;
+ break;
+
+ case kAsioLatenciesChanged:
+ // This will inform the host application that the drivers were latencies changed.
+ // Beware, it this does not mean that the buffer sizes have changed!
+ // You might need to update internal delay data.
+ ret = 1L;
+ //printf("kAsioLatenciesChanged \n");
+ break;
+
+ case kAsioEngineVersion:
+ // return the supported ASIO version of the host application
+ // If a host applications does not implement this selector, ASIO 1.0 is assumed
+ // by the driver
+ ret = 2L;
+ break;
+
+ case kAsioSupportsTimeInfo:
+ // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
+ // is supported.
+ // For compatibility with ASIO 1.0 drivers the host application should always support
+ // the "old" bufferSwitch method, too.
+ ret = 1;
+ break;
+
+ case kAsioSupportsTimeCode:
+ // informs the driver wether application is interested in time code info.
+ // If an application does not need to know about time code, the driver has less work
+ // to do.
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaAsioStream *stream = (PaAsioStream*)s;
+ ASIOError asioError;
+
+ if( stream->outputChannelCount > 0 )
+ {
+ ZeroOutputBuffers( stream, 0 );
+ ZeroOutputBuffers( stream, 1 );
+ }
+
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+ stream->stopProcessing = false;
+ stream->zeroOutput = false;
+
+ /* Reentrancy counter initialisation */
+ stream->reenterCount = -1;
+ stream->reenterError = 0;
+
+ stream->callbackFlags = 0;
+
+ if( ResetEvent( stream->completedBuffersPlayedEvent ) == 0 )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+
+ if( result == paNoError )
+ {
+ theAsioStream = stream;
+ asioError = ASIOStart();
+ if( asioError == ASE_OK )
+ {
+ stream->isActive = 1;
+ stream->streamFinishedCallbackCalled = false;
+ }
+ else
+ {
+ theAsioStream = 0;
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ }
+ }
+
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaAsioStream *stream = (PaAsioStream*)s;
+ ASIOError asioError;
+
+ if( stream->isActive )
+ {
+ stream->stopProcessing = true;
+
+ /* wait for the stream to finish playing out enqueued buffers.
+ timeout after four times the stream latency.
+
+ @todo should use a better time out value - if the user buffer
+ length is longer than the asio buffer size then that should
+ be taken into account.
+ */
+ if( WaitForSingleObject( theAsioStream->completedBuffersPlayedEvent,
+ (DWORD)(stream->streamRepresentation.streamInfo.outputLatency * 1000. * 4.) )
+ == WAIT_TIMEOUT )
+ {
+ PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n" ));
+ }
+ }
+
+ asioError = ASIOStop();
+ if( asioError != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ }
+
+ theAsioStream = 0;
+ stream->isActive = 0;
+
+ if( !stream->streamFinishedCallbackCalled )
+ {
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+
+ return result;
+}
+
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaAsioStream *stream = (PaAsioStream*)s;
+ ASIOError asioError;
+
+ stream->zeroOutput = true;
+
+ asioError = ASIOStop();
+ if( asioError != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ }
+ else
+ {
+ // make sure that the callback is not still in-flight when ASIOStop()
+ // returns. This has been observed to happen on the Hoontech DSP24 for
+ // example.
+ int count = 2000; // only wait for 2 seconds, rather than hanging.
+ while( theAsioStream->reenterCount != -1 && count > 0 )
+ {
+ Sleep(1);
+ --count;
+ }
+ }
+
+ /* it is questionable whether we should zero theAsioStream if ASIOStop()
+ returns an error, because the callback could still be active. We assume
+ not - this is based on the fact that ASIOStop is unlikely to fail
+ if the callback is running - it's more likely to fail because the
+ callback is not running. */
+
+ theAsioStream = 0;
+ stream->isActive = 0;
+
+ if( !stream->streamFinishedCallbackCalled )
+ {
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+
+ return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ //PaAsioStream *stream = (PaAsioStream*)s;
+ (void) s; /* unused parameter */
+ return theAsioStream == 0;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaAsioStream *stream = (PaAsioStream*)s;
+
+ return stream->isActive;
+}
+
+
+static PaTime GetStreamTime( PaStream *s )
+{
+ (void) s; /* unused parameter */
+ return (double)timeGetTime() * .001;
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaAsioStream *stream = (PaAsioStream*)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 )
+{
+ PaAsioStream *stream = (PaAsioStream*)s;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+ (void) stream; /* unused parameters */
+ (void) buffer;
+ (void) frames;
+
+ return paNoError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaAsioStream *stream = (PaAsioStream*)s;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+ (void) stream; /* unused parameters */
+ (void) buffer;
+ (void) frames;
+
+ return paNoError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaAsioStream *stream = (PaAsioStream*)s;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+ (void) stream; /* unused parameter */
+
+ return 0;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaAsioStream *stream = (PaAsioStream*)s;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+ (void) stream; /* unused parameter */
+
+ return 0;
+}
+
+
+PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
+{
+ PaError result = paNoError;
+ PaUtilHostApiRepresentation *hostApi;
+ PaDeviceIndex hostApiDevice;
+ ASIODriverInfo asioDriverInfo;
+ ASIOError asioError;
+ int asioIsInitialized = 0;
+ PaAsioHostApiRepresentation *asioHostApi;
+ PaAsioDeviceInfo *asioDeviceInfo;
+
+
+ result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
+ if( result != paNoError )
+ goto error;
+
+ result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
+ if( result != paNoError )
+ goto error;
+
+ /*
+ In theory we could proceed if the currently open device was the same
+ one for which the control panel was requested, however because the
+ window pointer is not available until this function is called we
+ currently need to call ASIOInit() again here, which of course can't be
+ done safely while a stream is open.
+ */
+
+ asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+ if( asioHostApi->openAsioDeviceIndex != paNoDevice )
+ {
+ result = paDeviceUnavailable;
+ goto error;
+ }
+
+ asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
+
+ if( !loadAsioDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
+ {
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+
+ /* CRUCIAL!!! */
+ memset( &asioDriverInfo, 0, sizeof(ASIODriverInfo) );
+ asioDriverInfo.asioVersion = 2;
+ asioDriverInfo.sysRef = systemSpecific;
+ asioError = ASIOInit( &asioDriverInfo );
+ if( asioError != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ goto error;
+ }
+ else
+ {
+ asioIsInitialized = 1;
+ }
+
+PA_DEBUG(("PaAsio_ShowControlPanel: ASIOInit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
+PA_DEBUG(("asioVersion: ASIOInit(): %ld\n", asioDriverInfo.asioVersion ));
+PA_DEBUG(("driverVersion: ASIOInit(): %ld\n", asioDriverInfo.driverVersion ));
+PA_DEBUG(("Name: ASIOInit(): %s\n", asioDriverInfo.name ));
+PA_DEBUG(("ErrorMessage: ASIOInit(): %s\n", asioDriverInfo.errorMessage ));
+
+ asioError = ASIOControlPanel();
+ if( asioError != ASE_OK )
+ {
+ PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ goto error;
+ }
+
+PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
+
+ asioError = ASIOExit();
+ if( asioError != ASE_OK )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ asioIsInitialized = 0;
+ goto error;
+ }
+
+PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
+
+ return result;
+
+error:
+ if( asioIsInitialized )
+ ASIOExit();
+
+ return result;
+}
+
diff --git a/pd/portaudio/pa_asio/pa_asio.h b/pd/portaudio/pa_asio/pa_asio.h
new file mode 100644
index 00000000..aed6f07b
--- /dev/null
+++ b/pd/portaudio/pa_asio/pa_asio.h
@@ -0,0 +1,79 @@
+#ifndef PA_ASIO_H
+#define PA_ASIO_H
+/*
+ * $Id: pa_asio.h,v 1.1.2.5 2003/09/20 21:06:44 rossbencina Exp $
+ * 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 );
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* PA_ASIO_H */
diff --git a/pd/portaudio/pa_common/pa_allocation.c b/pd/portaudio/pa_common/pa_allocation.c
new file mode 100644
index 00000000..8bc7c72f
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_allocation.c
@@ -0,0 +1,224 @@
+/*
+ * $Id: pa_allocation.c,v 1.1.2.5 2003/09/20 21:04:44 rossbencina Exp $
+ * 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 )
+ {
+ previous->next = current->next;
+
+ current->buffer = 0;
+ current->next = group->spareLinks;
+ group->spareLinks = current;
+ }
+ 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;
+ }
+}
+
diff --git a/pd/portaudio/pa_common/pa_allocation.h b/pd/portaudio/pa_common/pa_allocation.h
new file mode 100644
index 00000000..fb9321a0
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_allocation.h
@@ -0,0 +1,95 @@
+#ifndef PA_ALLOCATION_H
+#define PA_ALLOCATION_H
+/*
+ * $Id: pa_allocation.h,v 1.1.2.4 2003/09/20 21:04:44 rossbencina Exp $
+ * 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 */
diff --git a/pd/portaudio/pa_common/pa_converters.c b/pd/portaudio/pa_common/pa_converters.c
new file mode 100644
index 00000000..28ea106f
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_converters.c
@@ -0,0 +1,1925 @@
+/*
+ * $Id: pa_converters.c,v 1.1.2.25 2004/04/15 10:05:06 rossbencina Exp $
+ * 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 implementations.
+
+ If the C9x function lrintf() is available, define PA_USE_C99_LRINTF to use it
+
+ @todo Consider whether functions which dither but don't clip should exist,
+ V18 automatically enabled clipping whenever dithering was selected. Perhaps
+ we should do the same.
+
+ @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
+ Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
+ Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither,
+ Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither,
+
+ @todo review the converters marked REVIEW: Float32_To_Int32,
+ Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip,
+ Int32_To_Int16_Dither, Int32_To_Int8_Dither, Int16_To_Int32
+*/
+
+
+#include "pa_converters.h"
+#include "pa_dither.h"
+#include "pa_endianness.h"
+
+
+PaSampleFormat PaUtil_SelectClosestAvailableFormat(
+ PaSampleFormat availableFormats, PaSampleFormat format )
+{
+ PaSampleFormat result;
+
+ format &= ~paNonInterleaved;
+ availableFormats &= ~paNonInterleaved;
+
+ if( (format & availableFormats) == 0 )
+ {
+ /* NOTE: this code depends on the sample format constants being in
+ descending order of quality - ie best quality is 0
+ FIXME: should write an assert which checks that all of the
+ known constants conform to that requirement.
+ */
+
+ if( format != 0x01 )
+ {
+ /* scan for better formats */
+ result = format;
+ do
+ {
+ result >>= 1;
+ }
+ while( (result & availableFormats) == 0 && result != 0 );
+ }
+ else
+ {
+ result = 0;
+ }
+
+ if( result == 0 ){
+ /* scan for worse formats */
+ result = format;
+ do
+ {
+ result <<= 1;
+ }
+ while( (result & availableFormats) == 0 && result != paCustomFormat );
+
+ if( (result & availableFormats) == 0 )
+ result = paSampleFormatNotSupported;
+ }
+
+ }else{
+ result = format;
+ }
+
+ return result;
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_FORMAT_( format, float32, int32, int24, int16, int8, uint8 ) \
+ switch( format & ~paNonInterleaved ){ \
+ case paFloat32: \
+ float32 \
+ case paInt32: \
+ int32 \
+ case paInt24: \
+ int24 \
+ case paInt16: \
+ int16 \
+ case paInt8: \
+ int8 \
+ case paUInt8: \
+ uint8 \
+ default: return 0; \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_CLIP_( flags, source, destination ) \
+ if( flags & paClipOff ){ /* no clip */ \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _Dither; \
+ } \
+ }else{ /* clip */ \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination ## _Clip; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _DitherClip; \
+ } \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_SELECT_CONVERTER_DITHER_( flags, source, destination ) \
+ if( flags & paDitherOff ){ /* no dither */ \
+ return paConverters. source ## _To_ ## destination; \
+ }else{ /* dither */ \
+ return paConverters. source ## _To_ ## destination ## _Dither; \
+ }
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_USE_CONVERTER_( source, destination )\
+ return paConverters. source ## _To_ ## destination;
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_UNITY_CONVERSION_( wordlength )\
+ return paConverters. Copy_ ## wordlength ## _To_ ## wordlength;
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverter* PaUtil_SelectConverter( PaSampleFormat sourceFormat,
+ PaSampleFormat destinationFormat, PaStreamFlags flags )
+{
+ PA_SELECT_FORMAT_( sourceFormat,
+ /* paFloat32: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_UNITY_CONVERSION_( 32 ),
+ /* paInt32: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int32 ),
+ /* paInt24: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_CLIP_( flags, Float32, UInt8 )
+ ),
+ /* paInt32: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int32, Float32 ),
+ /* paInt32: */ PA_UNITY_CONVERSION_( 32 ),
+ /* paInt24: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int32, UInt8 )
+ ),
+ /* paInt24: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int24, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int24, Int32 ),
+ /* paInt24: */ PA_UNITY_CONVERSION_( 24 ),
+ /* paInt16: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int24, UInt8 )
+ ),
+ /* paInt16: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int16, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int16, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( Int16, Int24 ),
+ /* paInt16: */ PA_UNITY_CONVERSION_( 16 ),
+ /* paInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, Int8 ),
+ /* paUInt8: */ PA_SELECT_CONVERTER_DITHER_( flags, Int16, UInt8 )
+ ),
+ /* paInt8: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( Int8, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( Int8, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( Int8, Int24 ),
+ /* paInt16: */ PA_USE_CONVERTER_( Int8, Int16 ),
+ /* paInt8: */ PA_UNITY_CONVERSION_( 8 ),
+ /* paUInt8: */ PA_USE_CONVERTER_( Int8, UInt8 )
+ ),
+ /* paUInt8: */
+ PA_SELECT_FORMAT_( destinationFormat,
+ /* paFloat32: */ PA_USE_CONVERTER_( UInt8, Float32 ),
+ /* paInt32: */ PA_USE_CONVERTER_( UInt8, Int32 ),
+ /* paInt24: */ PA_USE_CONVERTER_( UInt8, Int24 ),
+ /* paInt16: */ PA_USE_CONVERTER_( UInt8, Int16 ),
+ /* paInt8: */ PA_USE_CONVERTER_( UInt8, Int8 ),
+ /* paUInt8: */ PA_UNITY_CONVERSION_( 8 )
+ )
+ )
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_CONVERTERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+ 0, /* PaUtilConverter *Float32_To_Int32; */
+ 0, /* PaUtilConverter *Float32_To_Int32_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int32_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int24; */
+ 0, /* PaUtilConverter *Float32_To_Int24_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int24_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int16; */
+ 0, /* PaUtilConverter *Float32_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int16_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_Int8; */
+ 0, /* PaUtilConverter *Float32_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Float32_To_Int8_Clip; */
+ 0, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+ 0, /* PaUtilConverter *Float32_To_UInt8; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_Dither; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_Clip; */
+ 0, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+ 0, /* PaUtilConverter *Int32_To_Float32; */
+ 0, /* PaUtilConverter *Int32_To_Int24; */
+ 0, /* PaUtilConverter *Int32_To_Int24_Dither; */
+ 0, /* PaUtilConverter *Int32_To_Int16; */
+ 0, /* PaUtilConverter *Int32_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Int32_To_Int8; */
+ 0, /* PaUtilConverter *Int32_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int32_To_UInt8; */
+ 0, /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int24_To_Float32; */
+ 0, /* PaUtilConverter *Int24_To_Int32; */
+ 0, /* PaUtilConverter *Int24_To_Int16; */
+ 0, /* PaUtilConverter *Int24_To_Int16_Dither; */
+ 0, /* PaUtilConverter *Int24_To_Int8; */
+ 0, /* PaUtilConverter *Int24_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int24_To_UInt8; */
+ 0, /* PaUtilConverter *Int24_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int16_To_Float32; */
+ 0, /* PaUtilConverter *Int16_To_Int32; */
+ 0, /* PaUtilConverter *Int16_To_Int24; */
+ 0, /* PaUtilConverter *Int16_To_Int8; */
+ 0, /* PaUtilConverter *Int16_To_Int8_Dither; */
+ 0, /* PaUtilConverter *Int16_To_UInt8; */
+ 0, /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+ 0, /* PaUtilConverter *Int8_To_Float32; */
+ 0, /* PaUtilConverter *Int8_To_Int32; */
+ 0, /* PaUtilConverter *Int8_To_Int24 */
+ 0, /* PaUtilConverter *Int8_To_Int16; */
+ 0, /* PaUtilConverter *Int8_To_UInt8; */
+
+ 0, /* PaUtilConverter *UInt8_To_Float32; */
+ 0, /* PaUtilConverter *UInt8_To_Int32; */
+ 0, /* PaUtilConverter *UInt8_To_Int24; */
+ 0, /* PaUtilConverter *UInt8_To_Int16; */
+ 0, /* PaUtilConverter *UInt8_To_Int8; */
+
+ 0, /* PaUtilConverter *Copy_8_To_8; */
+ 0, /* PaUtilConverter *Copy_16_To_16; */
+ 0, /* PaUtilConverter *Copy_24_To_24; */
+ 0 /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_CONVERTERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+#define PA_CLIP_( val, min, max )\
+ { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); }
+
+
+static const float const_1_div_128_ = 1.0f / 128.0f; /* 8 bit multiplier */
+
+static const float const_1_div_32768_ = 1.0f / 32768.f; /* 16 bit multiplier */
+
+static const double const_1_div_2147483648_ = 1.0 / 2147483648.0; /* 32 bit multiplier */
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float scaled = *src * 0x7FFFFFFF;
+ *dest = lrintf(scaled-0.5f);
+#else
+ double scaled = *src * 0x7FFFFFFF;
+ *dest = (signed long) scaled;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = ((float)*src * (2147483646.0f)) + dither;
+ *dest = lrintf(dithered - 0.5f);
+#else
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ *dest = (signed long) dithered;
+#endif
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648.f, 2147483647.f );
+ *dest = lrintf(scaled-0.5f);
+#else
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ *dest = (signed long) scaled;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int32_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+
+ while( count-- )
+ {
+ /* REVIEW */
+#ifdef PA_USE_C99_LRINTF
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = ((float)*src * (2147483646.0f)) + dither;
+ PA_CLIP_( dithered, -2147483648.f, 2147483647.f );
+ *dest = lrintf(dithered-0.5f);
+#else
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+ *dest = (signed long) dithered;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+ double scaled = *src * 0x7FFFFFFF;
+ temp = (signed long) scaled;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+
+ temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+ double scaled = *src * 0x7FFFFFFF;
+ PA_CLIP_( scaled, -2147483648., 2147483647. );
+ temp = (signed long) scaled;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int24_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed long temp;
+
+ while( count-- )
+ {
+ /* convert to 32 bit and drop the low 8 bits */
+
+ double dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ double dithered = ((double)*src * (2147483646.0)) + dither;
+ PA_CLIP_( dithered, -2147483648., 2147483647. );
+
+ temp = (signed long) dithered;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 24);
+ dest[1] = (unsigned char)(temp >> 16);
+ dest[2] = (unsigned char)(temp >> 8);
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+#ifdef PA_USE_C99_LRINTF
+ float tempf = (*src * (32767.0f)) ;
+ *dest = lrintf(tempf-0.5f);
+#else
+ short samp = (short) (*src * (32767.0f));
+ *dest = samp;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (32766.0f)) + dither;
+
+#ifdef PA_USE_C99_LRINTF
+ *dest = lrintf(dithered-0.5f);
+#else
+ *dest = (signed short) dithered;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+#ifdef PA_USE_C99_LRINTF
+ long samp = lrintf((*src * (32767.0f)) -0.5f);
+#else
+ long samp = (signed long) (*src * (32767.0f));
+#endif
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+ *dest = (signed short) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int16_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (32766.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x8000, 0x7FFF );
+#ifdef PA_USE_C99_LRINTF
+ *dest = lrintf(samp-0.5f);
+#else
+ *dest = (signed short) samp;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ signed char samp = (signed char) (*src * (127.0f));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (126.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ signed long samp = (signed long)(*src * (127.0f));
+ PA_CLIP_( samp, -0x80, 0x7F );
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_Int8_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
+ /* use smaller scaler to prevent overflow when we add the dither */
+ float dithered = (*src * (126.0f)) + dither;
+ signed long samp = (signed long) dithered;
+ PA_CLIP_( samp, -0x80, 0x7F );
+ *dest = (signed char) samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ unsigned char samp = (unsigned char)(128 + ((unsigned char) (*src * (127.0f))));
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_Clip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Float32_To_UInt8_DitherClip(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ float *src = (float*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (float) ((double)*src * const_1_div_2147483648_);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW */
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = (unsigned char)(*src >> 8);
+ dest[1] = (unsigned char)(*src >> 16);
+ dest[2] = (unsigned char)(*src >> 24);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(*src >> 24);
+ dest[1] = (unsigned char)(*src >> 16);
+ dest[2] = (unsigned char)(*src >> 8);
+#endif
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int24_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (signed short) ((*src) >> 16);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ signed long dither;
+
+ while( count-- )
+ {
+ /* REVIEW */
+ dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+ *dest = (signed short) ((((*src)>>1) + dither) >> 15);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = (signed char) ((*src) >> 24);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ signed long dither;
+
+ while( count-- )
+ {
+ /* REVIEW */
+ dither = PaUtil_Generate16BitTriangularDither( ditherGenerator );
+ *dest = (signed char) ((((*src)>>1) + dither) >> 23);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(((*src) >> 24) + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int32_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed long *src = (signed long*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ temp = (((long)src[0]) << 8);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+ temp = (((long)src[0]) << 24);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 8);
+#endif
+
+ *dest = (float) ((double)temp * const_1_div_2147483648_);
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed long *dest = (signed long*) destinationBuffer;
+ signed long temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ temp = (((long)src[0]) << 8);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 24);
+#elif defined(PA_BIG_ENDIAN)
+ temp = (((long)src[0]) << 24);
+ temp = temp | (((long)src[1]) << 16);
+ temp = temp | (((long)src[2]) << 8);
+#endif
+
+ *dest = temp;
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+
+ signed short temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ temp = (((signed short)src[1]));
+ temp = temp | (signed short)(((signed short)src[2]) << 8);
+#elif defined(PA_BIG_ENDIAN)
+ /* src[2] is discarded */
+ temp = (signed short)(((signed short)src[0]) << 8);
+ temp = temp | (((signed short)src[1]));
+#endif
+
+ *dest = temp;
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int16_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ /* src[1] is discarded */
+ *dest = src[2];
+#elif defined(PA_BIG_ENDIAN)
+ /* src[2] is discarded */
+ /* src[1] is discarded */
+ *dest = src[0];
+#endif
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ /* src[0] is discarded */
+ /* src[1] is discarded */
+ *dest = (unsigned char)(src[2] + 128);
+#elif defined(PA_BIG_ENDIAN)
+ *dest = (unsigned char)(src[0] + 128);
+ /* src[1] is discarded */
+ /* src[2] is discarded */
+#endif
+
+ src += sourceStride * 3;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int24_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ (void) destinationBuffer; /* unused parameters */
+ (void) destinationStride; /* unused parameters */
+ (void) sourceBuffer; /* unused parameters */
+ (void) sourceStride; /* unused parameters */
+ (void) count; /* unused parameters */
+ (void) ditherGenerator; /* unused parameters */
+ /* IMPLEMENT ME */
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = *src * const_1_div_32768_; /* FIXME: i'm concerned about this being asymetrical with float->int16 -rb */
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* REVIEW: we should consider something like
+ (*src << 16) | (*src & 0xFFFF)
+ */
+
+ *dest = *src << 16;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*) sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ signed short temp;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ temp = *src;
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = (unsigned char)(temp);
+ dest[2] = (unsigned char)(temp >> 8);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(temp >> 8);
+ dest[1] = (unsigned char)(temp);
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed char)((*src) >> 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_Int8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(((*src) >> 8) + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int16_To_UInt8_Dither(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed short *src = (signed short*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ /* IMPLEMENT ME */
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = *src * const_1_div_128_;
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (*src) << 24;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = (*src);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (*src);
+ dest[1] = 0;
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed short)((*src) << 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Int8_To_UInt8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ signed char *src = (signed char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (unsigned char)(*src + 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Float32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ float *dest = (float*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ float samp = (*src - 128) * const_1_div_128_;
+ *dest = samp;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed long *dest = (signed long*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (*src - 128) << 24;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+ (void) ditherGenerator; /* unused parameters */
+
+ while( count-- )
+ {
+
+#if defined(PA_LITTLE_ENDIAN)
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = (unsigned char)(*src - 128);
+#elif defined(PA_BIG_ENDIAN)
+ dest[0] = (unsigned char)(*src - 128);
+ dest[1] = 0;
+ dest[2] = 0;
+#endif
+
+ src += sourceStride;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed short *dest = (signed short*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed short)((*src - 128) << 8);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void UInt8_To_Int8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ signed char *dest = (signed char*)destinationBuffer;
+ (void)ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ (*dest) = (signed char)(*src - 128);
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_8_To_8(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_16_To_16(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned short *src = (unsigned short*)sourceBuffer;
+ unsigned short *dest = (unsigned short*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_24_To_24(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned char *src = (unsigned char*)sourceBuffer;
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+
+ src += sourceStride * 3;
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Copy_32_To_32(
+ void *destinationBuffer, signed int destinationStride,
+ void *sourceBuffer, signed int sourceStride,
+ unsigned int count, struct PaUtilTriangularDitherGenerator *ditherGenerator )
+{
+ unsigned long *dest = (unsigned long*)destinationBuffer;
+ unsigned long *src = (unsigned long*)sourceBuffer;
+
+ (void) ditherGenerator; /* unused parameter */
+
+ while( count-- )
+ {
+ *dest = *src;
+
+ src += sourceStride;
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilConverterTable paConverters = {
+ Float32_To_Int32, /* PaUtilConverter *Float32_To_Int32; */
+ Float32_To_Int32_Dither, /* PaUtilConverter *Float32_To_Int32_Dither; */
+ Float32_To_Int32_Clip, /* PaUtilConverter *Float32_To_Int32_Clip; */
+ Float32_To_Int32_DitherClip, /* PaUtilConverter *Float32_To_Int32_DitherClip; */
+
+ Float32_To_Int24, /* PaUtilConverter *Float32_To_Int24; */
+ Float32_To_Int24_Dither, /* PaUtilConverter *Float32_To_Int24_Dither; */
+ Float32_To_Int24_Clip, /* PaUtilConverter *Float32_To_Int24_Clip; */
+ Float32_To_Int24_DitherClip, /* PaUtilConverter *Float32_To_Int24_DitherClip; */
+
+ Float32_To_Int16, /* PaUtilConverter *Float32_To_Int16; */
+ Float32_To_Int16_Dither, /* PaUtilConverter *Float32_To_Int16_Dither; */
+ Float32_To_Int16_Clip, /* PaUtilConverter *Float32_To_Int16_Clip; */
+ Float32_To_Int16_DitherClip, /* PaUtilConverter *Float32_To_Int16_DitherClip; */
+
+ Float32_To_Int8, /* PaUtilConverter *Float32_To_Int8; */
+ Float32_To_Int8_Dither, /* PaUtilConverter *Float32_To_Int8_Dither; */
+ Float32_To_Int8_Clip, /* PaUtilConverter *Float32_To_Int8_Clip; */
+ Float32_To_Int8_DitherClip, /* PaUtilConverter *Float32_To_Int8_DitherClip; */
+
+ Float32_To_UInt8, /* PaUtilConverter *Float32_To_UInt8; */
+ Float32_To_UInt8_Dither, /* PaUtilConverter *Float32_To_UInt8_Dither; */
+ Float32_To_UInt8_Clip, /* PaUtilConverter *Float32_To_UInt8_Clip; */
+ Float32_To_UInt8_DitherClip, /* PaUtilConverter *Float32_To_UInt8_DitherClip; */
+
+ Int32_To_Float32, /* PaUtilConverter *Int32_To_Float32; */
+ Int32_To_Int24, /* PaUtilConverter *Int32_To_Int24; */
+ Int32_To_Int24_Dither, /* PaUtilConverter *Int32_To_Int24_Dither; */
+ Int32_To_Int16, /* PaUtilConverter *Int32_To_Int16; */
+ Int32_To_Int16_Dither, /* PaUtilConverter *Int32_To_Int16_Dither; */
+ Int32_To_Int8, /* PaUtilConverter *Int32_To_Int8; */
+ Int32_To_Int8_Dither, /* PaUtilConverter *Int32_To_Int8_Dither; */
+ Int32_To_UInt8, /* PaUtilConverter *Int32_To_UInt8; */
+ Int32_To_UInt8_Dither, /* PaUtilConverter *Int32_To_UInt8_Dither; */
+
+ Int24_To_Float32, /* PaUtilConverter *Int24_To_Float32; */
+ Int24_To_Int32, /* PaUtilConverter *Int24_To_Int32; */
+ Int24_To_Int16, /* PaUtilConverter *Int24_To_Int16; */
+ Int24_To_Int16_Dither, /* PaUtilConverter *Int24_To_Int16_Dither; */
+ Int24_To_Int8, /* PaUtilConverter *Int24_To_Int8; */
+ Int24_To_Int8_Dither, /* PaUtilConverter *Int24_To_Int8_Dither; */
+ Int24_To_UInt8, /* PaUtilConverter *Int24_To_UInt8; */
+ Int24_To_UInt8_Dither, /* PaUtilConverter *Int24_To_UInt8_Dither; */
+
+ Int16_To_Float32, /* PaUtilConverter *Int16_To_Float32; */
+ Int16_To_Int32, /* PaUtilConverter *Int16_To_Int32; */
+ Int16_To_Int24, /* PaUtilConverter *Int16_To_Int24; */
+ Int16_To_Int8, /* PaUtilConverter *Int16_To_Int8; */
+ Int16_To_Int8_Dither, /* PaUtilConverter *Int16_To_Int8_Dither; */
+ Int16_To_UInt8, /* PaUtilConverter *Int16_To_UInt8; */
+ Int16_To_UInt8_Dither, /* PaUtilConverter *Int16_To_UInt8_Dither; */
+
+ Int8_To_Float32, /* PaUtilConverter *Int8_To_Float32; */
+ Int8_To_Int32, /* PaUtilConverter *Int8_To_Int32; */
+ Int8_To_Int24, /* PaUtilConverter *Int8_To_Int24 */
+ Int8_To_Int16, /* PaUtilConverter *Int8_To_Int16; */
+ Int8_To_UInt8, /* PaUtilConverter *Int8_To_UInt8; */
+
+ UInt8_To_Float32, /* PaUtilConverter *UInt8_To_Float32; */
+ UInt8_To_Int32, /* PaUtilConverter *UInt8_To_Int32; */
+ UInt8_To_Int24, /* PaUtilConverter *UInt8_To_Int24; */
+ UInt8_To_Int16, /* PaUtilConverter *UInt8_To_Int16; */
+ UInt8_To_Int8, /* PaUtilConverter *UInt8_To_Int8; */
+
+ Copy_8_To_8, /* PaUtilConverter *Copy_8_To_8; */
+ Copy_16_To_16, /* PaUtilConverter *Copy_16_To_16; */
+ Copy_24_To_24, /* PaUtilConverter *Copy_24_To_24; */
+ Copy_32_To_32 /* PaUtilConverter *Copy_32_To_32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_CONVERTERS */
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroer* PaUtil_SelectZeroer( PaSampleFormat destinationFormat )
+{
+ switch( destinationFormat & ~paNonInterleaved ){
+ case paFloat32:
+ return paZeroers.Zero32;
+ case paInt32:
+ return paZeroers.Zero32;
+ case paInt24:
+ return paZeroers.Zero24;
+ case paInt16:
+ return paZeroers.Zero16;
+ case paInt8:
+ return paZeroers.Zero8;
+ case paUInt8:
+ return paZeroers.ZeroU8;
+ default: return 0;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+#ifdef PA_NO_STANDARD_ZEROERS
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+ 0, /* PaUtilZeroer *ZeroU8; */
+ 0, /* PaUtilZeroer *Zero8; */
+ 0, /* PaUtilZeroer *Zero16; */
+ 0, /* PaUtilZeroer *Zero24; */
+ 0, /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#else /* PA_NO_STANDARD_ZEROERS is not defined */
+
+/* -------------------------------------------------------------------------- */
+
+static void ZeroU8( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 128;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero8( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero16( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned short *dest = (unsigned short*)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero24( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned char *dest = (unsigned char*)destinationBuffer;
+
+ while( count-- )
+ {
+ dest[0] = 0;
+ dest[1] = 0;
+ dest[2] = 0;
+
+ dest += destinationStride * 3;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void Zero32( void *destinationBuffer, signed int destinationStride,
+ unsigned int count )
+{
+ unsigned long *dest = (unsigned long*)destinationBuffer;
+
+ while( count-- )
+ {
+ *dest = 0;
+
+ dest += destinationStride;
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+PaUtilZeroerTable paZeroers = {
+ ZeroU8, /* PaUtilZeroer *ZeroU8; */
+ Zero8, /* PaUtilZeroer *Zero8; */
+ Zero16, /* PaUtilZeroer *Zero16; */
+ Zero24, /* PaUtilZeroer *Zero24; */
+ Zero32, /* PaUtilZeroer *Zero32; */
+};
+
+/* -------------------------------------------------------------------------- */
+
+#endif /* PA_NO_STANDARD_ZEROERS */
diff --git a/pd/portaudio/pa_common/pa_converters.h b/pd/portaudio/pa_common/pa_converters.h
new file mode 100644
index 00000000..831c9c64
--- /dev/null
+++ b/pd/portaudio/pa_common/pa_converters.h
@@ -0,0 +1,254 @@
+#ifndef PA_CONVERTERS_H
+#define PA_CONVERTERS_H
+/*
+ * $Id: pa_converters.h,v 1.1.2.9 2003/09/20 21:05:14 rossbencina Exp $
+ * 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 */