From 9c0e19a3be2288db79e2502e5fa450c3e20a668d Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Fri, 9 May 2003 16:04:00 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r610, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=611 --- pd/portaudio/MSP-README.txt | 7 +- pd/portaudio/Makefile.in | 132 + pd/portaudio/Makefile.linux | 59 + pd/portaudio/Makefile.mingw | 57 + pd/portaudio/README.txt | 2 +- pd/portaudio/V19-devel-readme.txt | 230 + pd/portaudio/aclocal.m4 | 57 + pd/portaudio/config.doxy | 185 + pd/portaudio/config.guess | 1308 ++++++ pd/portaudio/configure | 2903 +++++++++++++ pd/portaudio/configure.in | 123 + pd/portaudio/docs/index.html | 60 + pd/portaudio/docs/latency.html | 192 + pd/portaudio/docs/pa_impl_guide.html | 197 + pd/portaudio/docs/pa_impl_startstop.html | 190 + pd/portaudio/docs/pa_tut_asio.html | 55 + pd/portaudio/docs/pa_tut_callback.html | 91 + pd/portaudio/docs/pa_tut_devs.html | 65 + pd/portaudio/docs/pa_tut_explore.html | 42 + pd/portaudio/docs/pa_tut_init.html | 43 + pd/portaudio/docs/pa_tut_mac.html | 41 + pd/portaudio/docs/pa_tut_mac_osx.html | 46 + pd/portaudio/docs/pa_tut_open.html | 52 + pd/portaudio/docs/pa_tut_oss.html | 46 + pd/portaudio/docs/pa_tut_over.html | 92 + pd/portaudio/docs/pa_tut_pc.html | 78 + pd/portaudio/docs/pa_tut_run.html | 56 + pd/portaudio/docs/pa_tut_rw.html | 79 + pd/portaudio/docs/pa_tut_term.html | 47 + pd/portaudio/docs/pa_tut_util.html | 55 + pd/portaudio/docs/pa_tutorial.html | 46 + pd/portaudio/docs/portaudio_h.txt | 425 ++ pd/portaudio/docs/portaudio_icmc2001.pdf | Bin 0 -> 50968 bytes pd/portaudio/docs/proposals.html | 36 + pd/portaudio/docs/releases.html | 339 ++ pd/portaudio/fixdir.bat | 19 + pd/portaudio/fixfile.bat | 7 + pd/portaudio/index.html | 89 + pd/portaudio/install-sh | 251 ++ pd/portaudio/pa_asio/Callback_adaptation_.pdf | Bin 0 -> 50527 bytes pd/portaudio/pa_asio/Pa_ASIO.pdf | Bin 0 -> 50778 bytes pd/portaudio/pa_asio/borland_asio_readme.txt | 6 + pd/portaudio/pa_asio/pa_asio.cpp | 4403 ++++++++------------ pd/portaudio/pa_asio/pa_asio.h | 68 + pd/portaudio/pa_asio/readme_asio_sdk_patch.txt | 25 + pd/portaudio/pa_beos/PlaybackNode.cc | 538 +++ pd/portaudio/pa_beos/PlaybackNode.h | 108 + pd/portaudio/pa_beos/pa_beos_mk.cc | 441 ++ pd/portaudio/pa_common/pa_allocation.c | 217 + pd/portaudio/pa_common/pa_allocation.h | 92 + pd/portaudio/pa_common/pa_allocation.o | Bin 0 -> 1744 bytes pd/portaudio/pa_common/pa_converters.c | 1653 ++++++++ pd/portaudio/pa_common/pa_converters.h | 197 + pd/portaudio/pa_common/pa_converters.o | Bin 0 -> 20384 bytes pd/portaudio/pa_common/pa_cpuload.c | 79 + pd/portaudio/pa_common/pa_cpuload.h | 56 + pd/portaudio/pa_common/pa_cpuload.o | Bin 0 -> 1452 bytes pd/portaudio/pa_common/pa_dither.c | 91 + pd/portaudio/pa_common/pa_dither.h | 189 + pd/portaudio/pa_common/pa_dither.o | Bin 0 -> 1136 bytes pd/portaudio/pa_common/pa_endianness.h | 108 + pd/portaudio/pa_common/pa_front.c | 1884 +++++++++ pd/portaudio/pa_common/pa_front.o | Bin 0 -> 12576 bytes pd/portaudio/pa_common/pa_hostapi.h | 238 ++ pd/portaudio/pa_common/pa_process.c | 1355 ++++++ pd/portaudio/pa_common/pa_process.h | 203 + pd/portaudio/pa_common/pa_process.o | Bin 0 -> 10192 bytes pd/portaudio/pa_common/pa_skeleton.c | 724 ++++ pd/portaudio/pa_common/pa_skeleton.o | Bin 0 -> 3468 bytes pd/portaudio/pa_common/pa_stream.c | 114 + pd/portaudio/pa_common/pa_stream.h | 128 + pd/portaudio/pa_common/pa_stream.o | Bin 0 -> 1059 bytes pd/portaudio/pa_common/pa_trace.c | 22 +- pd/portaudio/pa_common/pa_trace.h | 31 +- pd/portaudio/pa_common/pa_trace.o | Bin 0 -> 584 bytes pd/portaudio/pa_common/pa_util.h | 143 + pd/portaudio/pa_common/portaudio.h | 1189 ++++-- pd/portaudio/pa_dll_switch/PaDllEntry.h | 184 + .../pa_dll_switch/letter_from_tim_010817.txt | Bin 0 -> 1176 bytes pd/portaudio/pa_dll_switch/loadPA_DLL.cpp | 203 + pd/portaudio/pa_dll_switch/pa_lib.c | 827 ++++ pd/portaudio/pa_dll_switch/portaudio.h | 439 ++ pd/portaudio/pa_jack/pa_jack.c | 864 ++++ pd/portaudio/pa_linux_alsa/blocking_calls.c | 61 + pd/portaudio/pa_linux_alsa/blocking_calls.o | Bin 0 -> 1180 bytes pd/portaudio/pa_linux_alsa/callback_thread.c | 374 ++ pd/portaudio/pa_linux_alsa/callback_thread.o | Bin 0 -> 4180 bytes pd/portaudio/pa_linux_alsa/pa_linux_alsa.c | 989 +++++ pd/portaudio/pa_linux_alsa/pa_linux_alsa.h | 45 + pd/portaudio/pa_linux_alsa/pa_linux_alsa.o | Bin 0 -> 10728 bytes pd/portaudio/pa_mac_core/notes.txt | 14 +- pd/portaudio/pa_mac_core/pa_mac_core.c | 1514 +++---- pd/portaudio/pa_mac_sm/pa_mac_sm.c | 1656 ++++++++ pd/portaudio/pa_sgi/Makefile | 51 + pd/portaudio/pa_sgi/pa_sgi.c | 999 +++++ pd/portaudio/pa_sgi/pthread-Makefile | 52 + pd/portaudio/pa_sgi/pthread-pa_sgi.c | 908 ++++ pd/portaudio/pa_tests/debug_convert.c | 131 + pd/portaudio/pa_tests/debug_dither_calc.c | 55 + pd/portaudio/pa_tests/debug_dual.c | 183 + pd/portaudio/pa_tests/debug_multi_in.c | 179 + pd/portaudio/pa_tests/debug_multi_out.c | 144 + pd/portaudio/pa_tests/debug_record.c | 339 ++ pd/portaudio/pa_tests/debug_record_reuse.c | 351 ++ pd/portaudio/pa_tests/debug_sine.c | 192 + pd/portaudio/pa_tests/debug_sine_amp.c | 157 + pd/portaudio/pa_tests/debug_sine_formats.c | 202 + pd/portaudio/pa_tests/debug_srate.c | 265 ++ pd/portaudio/pa_tests/debug_test1.c | 114 + pd/portaudio/pa_tests/pa_devs.c | 199 + pd/portaudio/pa_tests/pa_fuzz.c | 168 + pd/portaudio/pa_tests/pa_minlat.c | 176 + pd/portaudio/pa_tests/paqa_devs.c | 317 ++ pd/portaudio/pa_tests/paqa_errs.c | 330 ++ pd/portaudio/pa_tests/patest1.c | 119 + pd/portaudio/pa_tests/patest_buffer.c | 181 + pd/portaudio/pa_tests/patest_clip.c | 156 + pd/portaudio/pa_tests/patest_dither.c | 152 + pd/portaudio/pa_tests/patest_hang.c | 151 + pd/portaudio/pa_tests/patest_latency.c | 176 + pd/portaudio/pa_tests/patest_leftright.c | 168 + pd/portaudio/pa_tests/patest_longsine.c | 137 + pd/portaudio/pa_tests/patest_many.c | 194 + pd/portaudio/pa_tests/patest_maxsines.c | 197 + pd/portaudio/pa_tests/patest_multi_sine.c | 175 + pd/portaudio/pa_tests/patest_pink.c | 245 ++ pd/portaudio/pa_tests/patest_record.c | 327 ++ pd/portaudio/pa_tests/patest_ringmix.c | 41 + pd/portaudio/pa_tests/patest_saw.c | 118 + pd/portaudio/pa_tests/patest_sine.c | 152 + pd/portaudio/pa_tests/patest_sine8.c | 184 + pd/portaudio/pa_tests/patest_sine_formats.c | 196 + pd/portaudio/pa_tests/patest_sine_time.c | 194 + pd/portaudio/pa_tests/patest_start_stop.c | 160 + pd/portaudio/pa_tests/patest_stop.c | 288 ++ pd/portaudio/pa_tests/patest_sync.c | 257 ++ pd/portaudio/pa_tests/patest_toomanysines.c | 172 + pd/portaudio/pa_tests/patest_underflow.c | 151 + pd/portaudio/pa_tests/patest_wire.c | 277 ++ pd/portaudio/pa_unix/pa_unix_hostapis.c | 60 + pd/portaudio/pa_unix/pa_unix_hostapis.o | Bin 0 -> 768 bytes pd/portaudio/pa_unix/pa_unix_util.c | 102 + pd/portaudio/pa_unix/pa_unix_util.o | Bin 0 -> 1096 bytes pd/portaudio/pa_unix_oss/Makefile | 43 + pd/portaudio/pa_unix_oss/Makefile_freebsd | 36 + pd/portaudio/pa_unix_oss/low_latency_tip.txt | Bin 0 -> 3111 bytes pd/portaudio/pa_unix_oss/pa_unix_oss.c | 1187 ++++++ pd/portaudio/pa_unix_oss/pa_unix_oss.o | Bin 0 -> 8548 bytes pd/portaudio/pa_unix_oss/recplay.c | 114 + pd/portaudio/pa_win/pa_win_hostapis.c | 63 + pd/portaudio/pa_win/pa_win_util.c | 128 + pd/portaudio/pa_win/pa_x86_plain_converters.c | 1167 ++++++ pd/portaudio/pa_win/pa_x86_plain_converters.h | 19 + pd/portaudio/pa_win_ds/dsound_wrapper.c | 604 +++ pd/portaudio/pa_win_ds/dsound_wrapper.h | 129 + pd/portaudio/pa_win_ds/pa_dsound.c | 1021 +++++ pd/portaudio/pa_win_ds/pa_win_ds.c | 1441 +++++++ pd/portaudio/pa_win_ds/portaudio.def | 28 + pd/portaudio/pa_win_wmme/Makefile.cygwin | 34 + pd/portaudio/pa_win_wmme/pa_win_wmme.c | 2672 ++++++++++++ pd/portaudio/pa_win_wmme/pa_win_wmme.h | 105 + pd/portaudio/pablio/pablio.h | 5 +- pd/portaudio/pablio/pablio_pd.c | 51 +- pd/portaudio/pablio/pablio_pd.h | 4 +- pd/portaudio/pablio/pablio_pd.o | Bin 0 -> 3500 bytes pd/portaudio/pablio/ringbuffer_pd.o | Bin 0 -> 1824 bytes pd/portaudio/testcvs/changeme.txt | 8 + 167 files changed, 44399 insertions(+), 3976 deletions(-) create mode 100644 pd/portaudio/Makefile.in create mode 100644 pd/portaudio/Makefile.linux create mode 100644 pd/portaudio/Makefile.mingw create mode 100644 pd/portaudio/V19-devel-readme.txt create mode 100644 pd/portaudio/aclocal.m4 create mode 100644 pd/portaudio/config.doxy create mode 100755 pd/portaudio/config.guess create mode 100755 pd/portaudio/configure create mode 100644 pd/portaudio/configure.in create mode 100644 pd/portaudio/docs/index.html create mode 100644 pd/portaudio/docs/latency.html create mode 100644 pd/portaudio/docs/pa_impl_guide.html create mode 100644 pd/portaudio/docs/pa_impl_startstop.html create mode 100644 pd/portaudio/docs/pa_tut_asio.html create mode 100644 pd/portaudio/docs/pa_tut_callback.html create mode 100644 pd/portaudio/docs/pa_tut_devs.html create mode 100644 pd/portaudio/docs/pa_tut_explore.html create mode 100644 pd/portaudio/docs/pa_tut_init.html create mode 100644 pd/portaudio/docs/pa_tut_mac.html create mode 100644 pd/portaudio/docs/pa_tut_mac_osx.html create mode 100644 pd/portaudio/docs/pa_tut_open.html create mode 100644 pd/portaudio/docs/pa_tut_oss.html create mode 100644 pd/portaudio/docs/pa_tut_over.html create mode 100644 pd/portaudio/docs/pa_tut_pc.html create mode 100644 pd/portaudio/docs/pa_tut_run.html create mode 100644 pd/portaudio/docs/pa_tut_rw.html create mode 100644 pd/portaudio/docs/pa_tut_term.html create mode 100644 pd/portaudio/docs/pa_tut_util.html create mode 100644 pd/portaudio/docs/pa_tutorial.html create mode 100644 pd/portaudio/docs/portaudio_h.txt create mode 100644 pd/portaudio/docs/portaudio_icmc2001.pdf create mode 100644 pd/portaudio/docs/proposals.html create mode 100644 pd/portaudio/docs/releases.html create mode 100755 pd/portaudio/fixdir.bat create mode 100755 pd/portaudio/fixfile.bat create mode 100644 pd/portaudio/index.html create mode 100755 pd/portaudio/install-sh create mode 100644 pd/portaudio/pa_asio/Callback_adaptation_.pdf create mode 100644 pd/portaudio/pa_asio/Pa_ASIO.pdf create mode 100644 pd/portaudio/pa_asio/borland_asio_readme.txt create mode 100644 pd/portaudio/pa_asio/pa_asio.h create mode 100755 pd/portaudio/pa_asio/readme_asio_sdk_patch.txt create mode 100644 pd/portaudio/pa_beos/PlaybackNode.cc create mode 100644 pd/portaudio/pa_beos/PlaybackNode.h create mode 100644 pd/portaudio/pa_beos/pa_beos_mk.cc create mode 100644 pd/portaudio/pa_common/pa_allocation.c create mode 100644 pd/portaudio/pa_common/pa_allocation.h create mode 100644 pd/portaudio/pa_common/pa_allocation.o create mode 100644 pd/portaudio/pa_common/pa_converters.c create mode 100644 pd/portaudio/pa_common/pa_converters.h create mode 100644 pd/portaudio/pa_common/pa_converters.o create mode 100644 pd/portaudio/pa_common/pa_cpuload.c create mode 100644 pd/portaudio/pa_common/pa_cpuload.h create mode 100644 pd/portaudio/pa_common/pa_cpuload.o create mode 100644 pd/portaudio/pa_common/pa_dither.c create mode 100644 pd/portaudio/pa_common/pa_dither.h create mode 100644 pd/portaudio/pa_common/pa_dither.o create mode 100644 pd/portaudio/pa_common/pa_endianness.h create mode 100644 pd/portaudio/pa_common/pa_front.c create mode 100644 pd/portaudio/pa_common/pa_front.o create mode 100644 pd/portaudio/pa_common/pa_hostapi.h create mode 100644 pd/portaudio/pa_common/pa_process.c create mode 100644 pd/portaudio/pa_common/pa_process.h create mode 100644 pd/portaudio/pa_common/pa_process.o create mode 100644 pd/portaudio/pa_common/pa_skeleton.c create mode 100644 pd/portaudio/pa_common/pa_skeleton.o create mode 100644 pd/portaudio/pa_common/pa_stream.c create mode 100644 pd/portaudio/pa_common/pa_stream.h create mode 100644 pd/portaudio/pa_common/pa_stream.o create mode 100644 pd/portaudio/pa_common/pa_trace.o create mode 100644 pd/portaudio/pa_common/pa_util.h create mode 100644 pd/portaudio/pa_dll_switch/PaDllEntry.h create mode 100644 pd/portaudio/pa_dll_switch/letter_from_tim_010817.txt create mode 100644 pd/portaudio/pa_dll_switch/loadPA_DLL.cpp create mode 100644 pd/portaudio/pa_dll_switch/pa_lib.c create mode 100644 pd/portaudio/pa_dll_switch/portaudio.h create mode 100644 pd/portaudio/pa_jack/pa_jack.c create mode 100644 pd/portaudio/pa_linux_alsa/blocking_calls.c create mode 100644 pd/portaudio/pa_linux_alsa/blocking_calls.o create mode 100644 pd/portaudio/pa_linux_alsa/callback_thread.c create mode 100644 pd/portaudio/pa_linux_alsa/callback_thread.o create mode 100644 pd/portaudio/pa_linux_alsa/pa_linux_alsa.c create mode 100644 pd/portaudio/pa_linux_alsa/pa_linux_alsa.h create mode 100644 pd/portaudio/pa_linux_alsa/pa_linux_alsa.o create mode 100644 pd/portaudio/pa_mac_sm/pa_mac_sm.c create mode 100644 pd/portaudio/pa_sgi/Makefile create mode 100644 pd/portaudio/pa_sgi/pa_sgi.c create mode 100644 pd/portaudio/pa_sgi/pthread-Makefile create mode 100644 pd/portaudio/pa_sgi/pthread-pa_sgi.c create mode 100644 pd/portaudio/pa_tests/debug_convert.c create mode 100644 pd/portaudio/pa_tests/debug_dither_calc.c create mode 100644 pd/portaudio/pa_tests/debug_dual.c create mode 100644 pd/portaudio/pa_tests/debug_multi_in.c create mode 100644 pd/portaudio/pa_tests/debug_multi_out.c create mode 100644 pd/portaudio/pa_tests/debug_record.c create mode 100644 pd/portaudio/pa_tests/debug_record_reuse.c create mode 100644 pd/portaudio/pa_tests/debug_sine.c create mode 100644 pd/portaudio/pa_tests/debug_sine_amp.c create mode 100644 pd/portaudio/pa_tests/debug_sine_formats.c create mode 100644 pd/portaudio/pa_tests/debug_srate.c create mode 100644 pd/portaudio/pa_tests/debug_test1.c create mode 100644 pd/portaudio/pa_tests/pa_devs.c create mode 100644 pd/portaudio/pa_tests/pa_fuzz.c create mode 100644 pd/portaudio/pa_tests/pa_minlat.c create mode 100644 pd/portaudio/pa_tests/paqa_devs.c create mode 100644 pd/portaudio/pa_tests/paqa_errs.c create mode 100644 pd/portaudio/pa_tests/patest1.c create mode 100644 pd/portaudio/pa_tests/patest_buffer.c create mode 100644 pd/portaudio/pa_tests/patest_clip.c create mode 100644 pd/portaudio/pa_tests/patest_dither.c create mode 100644 pd/portaudio/pa_tests/patest_hang.c create mode 100644 pd/portaudio/pa_tests/patest_latency.c create mode 100644 pd/portaudio/pa_tests/patest_leftright.c create mode 100644 pd/portaudio/pa_tests/patest_longsine.c create mode 100644 pd/portaudio/pa_tests/patest_many.c create mode 100644 pd/portaudio/pa_tests/patest_maxsines.c create mode 100644 pd/portaudio/pa_tests/patest_multi_sine.c create mode 100644 pd/portaudio/pa_tests/patest_pink.c create mode 100644 pd/portaudio/pa_tests/patest_record.c create mode 100644 pd/portaudio/pa_tests/patest_ringmix.c create mode 100644 pd/portaudio/pa_tests/patest_saw.c create mode 100644 pd/portaudio/pa_tests/patest_sine.c create mode 100644 pd/portaudio/pa_tests/patest_sine8.c create mode 100644 pd/portaudio/pa_tests/patest_sine_formats.c create mode 100644 pd/portaudio/pa_tests/patest_sine_time.c create mode 100644 pd/portaudio/pa_tests/patest_start_stop.c create mode 100644 pd/portaudio/pa_tests/patest_stop.c create mode 100644 pd/portaudio/pa_tests/patest_sync.c create mode 100644 pd/portaudio/pa_tests/patest_toomanysines.c create mode 100644 pd/portaudio/pa_tests/patest_underflow.c create mode 100644 pd/portaudio/pa_tests/patest_wire.c create mode 100644 pd/portaudio/pa_unix/pa_unix_hostapis.c create mode 100644 pd/portaudio/pa_unix/pa_unix_hostapis.o create mode 100644 pd/portaudio/pa_unix/pa_unix_util.c create mode 100644 pd/portaudio/pa_unix/pa_unix_util.o create mode 100644 pd/portaudio/pa_unix_oss/Makefile create mode 100644 pd/portaudio/pa_unix_oss/Makefile_freebsd create mode 100644 pd/portaudio/pa_unix_oss/low_latency_tip.txt create mode 100644 pd/portaudio/pa_unix_oss/pa_unix_oss.c create mode 100644 pd/portaudio/pa_unix_oss/pa_unix_oss.o create mode 100644 pd/portaudio/pa_unix_oss/recplay.c create mode 100644 pd/portaudio/pa_win/pa_win_hostapis.c create mode 100644 pd/portaudio/pa_win/pa_win_util.c create mode 100644 pd/portaudio/pa_win/pa_x86_plain_converters.c create mode 100644 pd/portaudio/pa_win/pa_x86_plain_converters.h create mode 100644 pd/portaudio/pa_win_ds/dsound_wrapper.c create mode 100644 pd/portaudio/pa_win_ds/dsound_wrapper.h create mode 100644 pd/portaudio/pa_win_ds/pa_dsound.c create mode 100644 pd/portaudio/pa_win_ds/pa_win_ds.c create mode 100644 pd/portaudio/pa_win_ds/portaudio.def create mode 100644 pd/portaudio/pa_win_wmme/Makefile.cygwin create mode 100644 pd/portaudio/pa_win_wmme/pa_win_wmme.c create mode 100644 pd/portaudio/pa_win_wmme/pa_win_wmme.h create mode 100644 pd/portaudio/pablio/pablio_pd.o create mode 100644 pd/portaudio/pablio/ringbuffer_pd.o create mode 100644 pd/portaudio/testcvs/changeme.txt (limited to 'pd/portaudio') diff --git a/pd/portaudio/MSP-README.txt b/pd/portaudio/MSP-README.txt index e29de09f..bd75626b 100644 --- a/pd/portaudio/MSP-README.txt +++ b/pd/portaudio/MSP-README.txt @@ -1,3 +1,6 @@ -This is not the full distribution, just what's needed to get Pd running -on Mac OSX and ASIO (Windows.) I changed some code in pablio.c as marked. +These files are from the V19 branch, a CVS snapshot, around 2003.03.21. + +I had to hack up "pablio" a fair bit... my copies are named "pablio_pd.c" +and so on. + -MSP diff --git a/pd/portaudio/Makefile.in b/pd/portaudio/Makefile.in new file mode 100644 index 00000000..18c2707f --- /dev/null +++ b/pd/portaudio/Makefile.in @@ -0,0 +1,132 @@ +# +# 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@ + +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/pa_devs \ + bin/pa_fuzz \ + bin/patest_sine \ + bin/patest1 + +# 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_latency \ + bin/patest_leftright \ + bin/patest_longsine \ + bin/patest_many \ + bin/patest_maxsines \ + bin/patest_multi_sine \ + bin/patest_pink \ + 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 + +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) + +%.o: %.c Makefile $(PAINC) + $(CC) -c $(CFLAGS) $< -o $@ + +bin: + mkdir bin + +lib: + mkdir lib + + + diff --git a/pd/portaudio/Makefile.linux b/pd/portaudio/Makefile.linux new file mode 100644 index 00000000..4deee60a --- /dev/null +++ b/pd/portaudio/Makefile.linux @@ -0,0 +1,59 @@ +# Make PortAudio for Linux +# Updated 2001/08/25 Bill Eldridge bill@rfa.org +# Updated 2001/10/16, philburk@softsynth.com, s/unix_oss/unix_oss/ +# Updated 2002/04/30 Bill Eldridge bill@rfa.org +# Made the libinstall and tests compile a bit cleaner + +# A pretty bare makefile, that figures out all the test files +# and compiles them against the library in the pa_unix_oss directory. + +# Do "make all" and then when happy, "make libinstall" +# (if not happy, "make clean") + +# The ldconfig stuff in libinstall is the wrong way to do it - +# someone tell me the right way, please + + +LIBS = -lm -lpthread + +CDEFINES = -I../pa_common +CFLAGS = -g +LIBINST = /usr/local/lib + +TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c) +TESTO:= $(wildcard pa_tests/pa*.o pa_tests/debug*.o) + +LIBFILES:= ./pa_common/pa_lib.c ./pa_unix_oss/pa_unix_oss.c + +#all: sharedlib libinstall tests +all: sharedlib libinstall testo testq + +.c.o: + -gcc -c -I./pa_common $< -o $*.o + +.o: + -gcc $*.o -o $* -Lpa_unix_oss $(LIBS) -lportaudio + +#.c.o: +# -gcc -c -I./pa_common $< -o $*.o +# -gcc $*.o -o $* -Lpa_unix_oss $(LIBS) -lportaudio + + +sharedlib: $(LIBFILES:.c=.o) + gcc -shared -o ./pa_unix_oss/libportaudio.so ./pa_common/pa_lib.o ./pa_unix_oss/pa_unix_oss.o + +libinstall: ./pa_unix_oss/libportaudio.so + @cp -f ./pa_unix_oss/libportaudio.so $(LIBINST) + @/sbin/ldconfig + +testo: $(TESTS:.c=.o) + +testq: $(TESTO:.o=) + +clean: + -@rm -f $(TESTS:.c=.o) + -@rm -f $(TESTS:.c=) + -@rm -f $(LIBFILES:.c=.o) + -@rm -f ./pa_unix_oss/libportaudio.so + + diff --git a/pd/portaudio/Makefile.mingw b/pd/portaudio/Makefile.mingw new file mode 100644 index 00000000..2043a161 --- /dev/null +++ b/pd/portaudio/Makefile.mingw @@ -0,0 +1,57 @@ + +# Makefile for PortAudio on mingw (http://mingw.sourceforge.net) + +# Contributed by Bill Eldridge, bill@rfa.org, Radio Free Asia +# Copyright 2002/02/20, GPL + +# Uses a common mingw32 cross-compiler that defaults +# to everything in /usr/local/cross-tools + +# First edit your path with +# export PATH=/usr/local/cross-tools/bin:$PATH + +# Usage: make -f Makefile.mingw all +# or make -f Makefile.mingw sharedlib +# make -f Makefile.mingw tests +# +# Then copy executables & portaudio.dll to your Windows machine +# +# To make work with pa_win_ds, you'll have to substitue +# all the pa_win_wmme files with pa_win_ds files, no biggie. + +CC= i586-mingw32msvc-gcc +DLLTOOL= i586-mingw32msvc-dlltool +DLLWRAP= i586-mingw32msvc-dllwrap + +ARCH= pa_win_wmme + +TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c) + +.c.o: + -$(CC) -c -I./pa_common $< -o $*.o + -$(CC) $*.o -o $*.exe -L/usr/local/lib -L$(ARCH) -lportaudio.dll -lwinmm + +all: sharedlib tests + +sharedlib: ./pa_common/pa_lib.c + $(CC) -c -I./pa_common pa_common/pa_lib.c -o pa_common/pa_lib.o + $(CC) -c -I./pa_common pa_win_wmme/pa_win_wmme.c -o pa_win_wmme/pa_win_wmme.o + $(CC) -shared -mthreads -o portaudio.dll pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/local/cross-tools/i586-win32msvc/lib -lwinmm -lm + $(DLLWRAP) --export-all --output-def=libportaudio.def --output-lib=libportaudio.a --dllname=portaudio.dll --drivername=i586-mingw32msvc-gcc pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/local/cross-tools/i586-win32msvc/lib -lwinmm -lm + $(CC) -shared -Wl,--enable-auto-image-base -o portaudio.dll -Wl,--out-implib=pa_win_wmme/libportaudio.dll.a pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/local/cross-tools/i586-win32msvc/lib -lwinmm + + +tests: $(TESTS:.c=.o) + +sine: + $(CC) -c -I./pa_common pa_tests/patest_sine.c -o pa_tests/patest_sine.o + $(CC) pa_tests/patest_sine.o -o pa_tests/patest_sine.exe -L/usr/local/lib -lportaudio.dll -lwinmm + +clean: + -rm ./pa_tests/*.exe + -rm ./pa_tests/*.o + +nothing: + $(CC) pa_tests/patest_sine.o -L/usr/lib/w32api -L./pa_win_wmme -lportaudio.dll -lwinmm + + diff --git a/pd/portaudio/README.txt b/pd/portaudio/README.txt index d1e5d7d6..4cfc6166 100644 --- a/pd/portaudio/README.txt +++ b/pd/portaudio/README.txt @@ -58,7 +58,7 @@ Important Files and Folders: Platform Implementations pa_asio = ASIO for Windows and Macintosh pa_beos = BeOS - pa_mac = Macintosh Sound Manager for OS 8,9 and Carbon + pa_mac_sm = Macintosh Sound Manager for OS 8,9 and Carbon pa_mac_core = Macintosh Core Audio for OS X pa_sgi = Silicon Graphics AL pa_unix_oss = OSS implementation for various Unixes diff --git a/pd/portaudio/V19-devel-readme.txt b/pd/portaudio/V19-devel-readme.txt new file mode 100644 index 00000000..a6bca780 --- /dev/null +++ b/pd/portaudio/V19-devel-readme.txt @@ -0,0 +1,230 @@ +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 following tests might run if you're lucky: + tests/pa_devs.c + tests/patest_sine.c + tests/pa_fuzz.c + + tests/patest1.c + +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_byteswappers.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_byteswappers.c/h + byte swapping 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_ 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_ 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/aclocal.m4 b/pd/portaudio/aclocal.m4 new file mode 100644 index 00000000..c80e0acf --- /dev/null +++ b/pd/portaudio/aclocal.m4 @@ -0,0 +1,57 @@ + +dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +dnl also defines GSTUFF_PKG_ERRORS on error +AC_DEFUN(PKG_CHECK_MODULES, [ + succeeded=no + + if test -z "$PKG_CONFIG"; then + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + $1_LIBS=`$PKG_CONFIG --libs "$2"` + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST($1_CFLAGS) + AC_SUBST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi +]) + + diff --git a/pd/portaudio/config.doxy b/pd/portaudio/config.doxy new file mode 100644 index 00000000..98342765 --- /dev/null +++ b/pd/portaudio/config.doxy @@ -0,0 +1,185 @@ +# Doxyfile 1.2.13-20020210 + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = PortAudio +PROJECT_NUMBER = 2.0 +OUTPUT_DIRECTORY = "./docs/" +OUTPUT_LANGUAGE = English +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = NO +STRIP_FROM_PATH = +INTERNAL_DOCS = NO +STRIP_CODE_COMMENTS = YES +CASE_SENSE_NAMES = YES +SHORT_NAMES = NO +HIDE_SCOPE_NAMES = NO +VERBATIM_HEADERS = YES +SHOW_INCLUDE_FILES = YES +JAVADOC_AUTOBRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +ALIASES = +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SHOW_USED_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ./pa_common ./pa_win_wmme ./pa_asio ./pa_win_ds +FILE_PATTERNS = *.h *.c *.cpp +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_SOURCE_FILES = NO +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = doxygen_html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = NO +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +TEMPLATE_RELATIONS = YES +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO +CGI_NAME = search.cgi +CGI_URL = +DOC_URL = +DOC_ABSPATH = +BIN_ABSPATH = /usr/local/bin/ +EXT_DOC_PATHS = diff --git a/pd/portaudio/config.guess b/pd/portaudio/config.guess new file mode 100755 index 00000000..297e5c30 --- /dev/null +++ b/pd/portaudio/config.guess @@ -0,0 +1,1308 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. + +timestamp='2001-10-05' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + + +dummy=dummy-$$ +trap 'rm -f $dummy.c $dummy.o $dummy.rel $dummy; exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +set_cc_for_build='case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int dummy(){}" > $dummy.c ; + for c in cc gcc c89 ; do + ($c $dummy.c -c -o $dummy.o) >/dev/null 2>&1 ; + if test $? = 0 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + rm -f $dummy.c $dummy.o $dummy.rel ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # Determine the machine/vendor (is the vendor relevant). + case "${UNAME_MACHINE}" in + amiga) machine=m68k-unknown ;; + arm32) machine=arm-unknown ;; + atari*) machine=m68k-atari ;; + sun3*) machine=m68k-sun ;; + mac68k) machine=m68k-apple ;; + macppc) machine=powerpc-apple ;; + hp3[0-9][05]) machine=m68k-hp ;; + ibmrt|romp-ibm) machine=romp-ibm ;; + sparc*) machine=`uname -p`-unknown ;; + *) machine=${UNAME_MACHINE}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE}" in + i386|sparc|amiga|arm*|hp300|mvme68k|vax|atari|luna68k|mac68k|news68k|next68k|pc532|sun3*|x68k) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mipseb-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + eval $set_cc_for_build + $CC_FOR_BUILD $dummy.s -o $dummy 2>/dev/null + if test "$?" = 0 ; then + case `./$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + 2-1307) + UNAME_MACHINE="alphaev68" + ;; + esac + fi + rm -f $dummy.s $dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy \ + && ./$dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | head -1 | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null) && HP_ARCH=`./$dummy` + if test -z "$HP_ARCH"; then HP_ARCH=hppa; fi + rm -f $dummy.c $dummy + fi ;; + esac + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD $dummy.c -o $dummy && ./$dummy && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3D:*:*:*) + echo alpha-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i386-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + case `sed -n '/^byte/s/^.*: \(.*\) endian/\1/p' < /proc/cpuinfo` in + big) echo mips-unknown-linux-gnu && exit 0 ;; + little) echo mipsel-unknown-linux-gnu && exit 0 ;; + esac + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + ld_supported_targets=`cd /; ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + cat >$dummy.c < +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-pc-linux-gnu\n", argv[1]); +# else + printf ("%s-pc-linux-gnulibc1\n", argv[1]); +# endif +# else + printf ("%s-pc-linux-gnulibc1\n", argv[1]); +# endif +#else + printf ("%s-pc-linux-gnuaout\n", argv[1]); +#endif + return 0; +} +EOF + $CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy "${UNAME_MACHINE}" && rm -f $dummy.c $dummy && exit 0 + rm -f $dummy.c $dummy + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|egrep '^Machine.*Pent ?II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|egrep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + echo `uname -p`-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + if test "${UNAME_MACHINE}" = "x86pc"; then + UNAME_MACHINE=pc + fi + echo `uname -p`-${UNAME_MACHINE}-nto-qnx + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[KW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD $dummy.c -o $dummy 2>/dev/null && ./$dummy && rm -f $dummy.c $dummy && exit 0 +rm -f $dummy.c $dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/pd/portaudio/configure b/pd/portaudio/configure new file mode 100755 index 00000000..1dfe83fc --- /dev/null +++ b/pd/portaudio/configure @@ -0,0 +1,2903 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by Autoconf 2.52. +# +# Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="sed y%*+%pp%;s%[^_$as_cr_alnum]%_%g" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="sed y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g" + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +cross_compiling=no +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +ac_unique_file="pa_common/portaudio.h" + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute path for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: should be removed in autoconf 3.0. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo "$ac_prog" | sed 's%[\\/][^\\/][^\\/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat < if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +EOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_subdir in : $ac_subdirs_all; do test "x$ac_subdir" = x: && continue + cd $ac_subdir + # A "../" for each directory in /$ac_subdir. + ac_dots=`echo $ac_subdir | + sed 's,^\./,,;s,[^/]$,&/,;s,[^/]*/,../,g'` + + case $srcdir in + .) # No --srcdir option. We are building in place. + ac_sub_srcdir=$srcdir ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_sub_srcdir=$srcdir/$ac_subdir ;; + *) # Relative path. + ac_sub_srcdir=$ac_dots$srcdir/$ac_subdir ;; + esac + + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_sub_srcdir/configure.gnu; then + echo + $SHELL $ac_sub_srcdir/configure.gnu --help=recursive + elif test -f $ac_sub_srcdir/configure; then + echo + $SHELL $ac_sub_srcdir/configure --help=recursive + elif test -f $ac_sub_srcdir/configure.ac || + test -f $ac_sub_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_subdir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\EOF + +Copyright 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +EOF + exit 0 +fi +exec 5>config.log +cat >&5 </dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +PATH = $PATH + +_ASUNAME +} >&5 + +cat >&5 <\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + ac_sep=" " ;; + *) ac_configure_args="$ac_configure_args$ac_sep$ac_arg" + ac_sep=" " ;; + esac + # Get rid of the leading space. +done + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + echo >&5 + echo "## ----------------- ##" >&5 + echo "## Cache variables. ##" >&5 + echo "## ----------------- ##" >&5 + echo >&5 + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} >&5 + sed "/^$/d" confdefs.h >conftest.log + if test -s conftest.log; then + echo >&5 + echo "## ------------ ##" >&5 + echo "## confdefs.h. ##" >&5 + echo "## ------------ ##" >&5 + echo >&5 + cat conftest.log >&5 + fi + (echo; echo) >&5 + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" >&5 + echo "$as_me: exit $exit_status" >&5 + rm -rf conftest* confdefs* core core.* *.core conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:821: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + cat "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:832: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:840: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:856: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:860: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:866: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:868: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:870: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. It doesn't matter if + # we pass some twice (in addition to the command line arguments). + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + *) ac_configure_args="$ac_configure_args $ac_var=$ac_new_val" + ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:889: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:891: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac +echo "#! $SHELL" >conftest.sh +echo "exit 0" >>conftest.sh +chmod +x conftest.sh +if { (echo "$as_me:911: PATH=\".;.\"; conftest.sh") >&5 + (PATH=".;."; conftest.sh) 2>&5 + ac_status=$? + echo "$as_me:914: \$? = $ac_status" >&5 + (exit $ac_status); }; then + ac_path_separator=';' +else + ac_path_separator=: +fi +PATH_SEPARATOR="$ac_path_separator" +rm -f conftest.sh + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:931: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="${ac_tool_prefix}gcc" +echo "$as_me:946: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:954: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:957: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:966: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="gcc" +echo "$as_me:981: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:989: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:992: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:1005: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="${ac_tool_prefix}cc" +echo "$as_me:1020: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1028: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1031: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:1040: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="cc" +echo "$as_me:1055: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:1063: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:1066: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:1079: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue +fi +ac_cv_prog_CC="cc" +echo "$as_me:1099: found $ac_dir/$ac_word" >&5 +break +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" ${1+"$@"} + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1121: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1124: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:1135: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_CC="$ac_tool_prefix$ac_prog" +echo "$as_me:1150: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:1158: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:1161: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:1174: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_CC="$ac_prog" +echo "$as_me:1189: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:1197: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:1200: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + +test -z "$CC" && { { echo "$as_me:1212: error: no acceptable cc found in \$PATH" >&5 +echo "$as_me: error: no acceptable cc found in \$PATH" >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:1217:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:1220: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:1223: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1225: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:1228: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:1230: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:1233: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +#line 1237 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:1253: checking for C compiler default output" >&5 +echo $ECHO_N "checking for C compiler default output... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:1256: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:1259: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. +for ac_file in `ls a.exe conftest.exe 2>/dev/null; + ls a.out conftest 2>/dev/null; + ls a.* conftest.* 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + a.out ) # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool --akim. + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1282: error: C compiler cannot create executables" >&5 +echo "$as_me: error: C compiler cannot create executables" >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:1288: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1293: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:1299: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1302: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:1309: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:1317: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:1324: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:1326: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:1329: checking for executable suffix" >&5 +echo $ECHO_N "checking for executable suffix... $ECHO_C" >&6 +if { (eval echo "$as_me:1331: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:1334: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in `(ls conftest.exe; ls conftest; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.o | *.obj | *.xcoff | *.tds | *.d | *.pdb ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:1350: error: cannot compute EXEEXT: cannot compile and link" >&5 +echo "$as_me: error: cannot compute EXEEXT: cannot compile and link" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:1356: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:1362: checking for object suffix" >&5 +echo $ECHO_N "checking for object suffix... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1368 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:1380: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1383: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +{ { echo "$as_me:1395: error: cannot compute OBJEXT: cannot compile" >&5 +echo "$as_me: error: cannot compute OBJEXT: cannot compile" >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:1402: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:1406: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1412 "configure" +#include "confdefs.h" + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1427: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1430: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1433: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1436: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_compiler_gnu=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:1448: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:1454: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line 1460 "configure" +#include "confdefs.h" + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1472: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1475: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1478: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1481: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_prog_cc_g=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:1491: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1518: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1521: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1524: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1527: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + ''\ + '#include ' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +#line 1539 "configure" +#include "confdefs.h" +#include +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1552: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1555: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1558: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1561: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +continue +fi +rm -f conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +#line 1571 "configure" +#include "confdefs.h" +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1583: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1586: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1589: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1592: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:1622: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" +echo "$as_me:1637: found $ac_dir/$ac_word" >&5 +break +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:1645: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:1648: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:1657: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + $as_executable_p "$ac_dir/$ac_word" || continue +ac_cv_prog_ac_ct_RANLIB="ranlib" +echo "$as_me:1672: found $ac_dir/$ac_word" >&5 +break +done + + test -z "$ac_cv_prog_ac_ct_RANLIB" && ac_cv_prog_ac_ct_RANLIB=":" +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:1681: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:1684: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:1710: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:1730: checking for a BSD compatible install" >&5 +echo $ECHO_N "checking for a BSD compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_IFS=$IFS; IFS=$ac_path_separator + for ac_dir in $PATH; do + IFS=$ac_save_IFS + # Account for people who put trailing slashes in PATH elements. + case $ac_dir/ in + / | ./ | .// | /cC/* \ + | /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* \ + | /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if $as_executable_p "$ac_dir/$ac_prog"; then + if test $ac_prog = install && + grep dspmsg "$ac_dir/$ac_prog" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$ac_dir/$ac_prog" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:1779: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +echo "$as_me:1792: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_AR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $AR in + [\\/]* | ?:[\\/]*) + ac_cv_path_AR="$AR" # Let the user override the test with a path. + ;; + *) + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + if $as_executable_p "$ac_dir/$ac_word"; then + ac_cv_path_AR="$ac_dir/$ac_word" + echo "$as_me:1809: found $ac_dir/$ac_word" >&5 + break +fi +done + + test -z "$ac_cv_path_AR" && ac_cv_path_AR="no" + ;; +esac +fi +AR=$ac_cv_path_AR + +if test -n "$AR"; then + echo "$as_me:1821: result: $AR" >&5 +echo "${ECHO_T}$AR" >&6 +else + echo "$as_me:1824: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +if [ $AR = "no" ] ; then + { { echo "$as_me:1829: error: \"Could not find ar - needed to create a library\"" >&5 +echo "$as_me: error: \"Could not find ar - needed to create a library\"" >&2;} + { (exit 1); exit 1; }; }; +fi + +echo "$as_me:1834: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_bigendian=unknown +# See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +#line 1842 "configure" +#include "confdefs.h" +#include +#include + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1859: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1862: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1865: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1868: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +#line 1872 "configure" +#include "confdefs.h" +#include +#include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:1889: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:1892: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:1895: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1898: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_c_bigendian=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +fi +rm -f conftest.$ac_objext conftest.$ac_ext +if test $ac_cv_c_bigendian = unknown; then +if test "$cross_compiling" = yes; then + { { echo "$as_me:1914: error: cannot run test program while cross compiling" >&5 +echo "$as_me: error: cannot run test program while cross compiling" >&2;} + { (exit 1); exit 1; }; } +else + cat >conftest.$ac_ext <<_ACEOF +#line 1919 "configure" +#include "confdefs.h" +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:1935: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:1938: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:1940: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:1943: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_c_bigendian=yes +fi +rm -f core core.* *.core conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:1956: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +if test $ac_cv_c_bigendian = yes; then + +cat >>confdefs.h <<\EOF +#define WORDS_BIGENDIAN 1 +EOF + +fi + +echo "$as_me:1966: checking for snd_pcm_open in -lasound" >&5 +echo $ECHO_N "checking for snd_pcm_open in -lasound... $ECHO_C" >&6 +if test "${ac_cv_lib_asound_snd_pcm_open+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lasound $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 1974 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char snd_pcm_open (); +int +main () +{ +snd_pcm_open (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:1993: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:1996: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:1999: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2002: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_asound_snd_pcm_open=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_asound_snd_pcm_open=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:2013: result: $ac_cv_lib_asound_snd_pcm_open" >&5 +echo "${ECHO_T}$ac_cv_lib_asound_snd_pcm_open" >&6 +if test $ac_cv_lib_asound_snd_pcm_open = yes; then + have_alsa=yes +else + have_alsa=no +fi + + succeeded=no + + if test -z "$PKG_CONFIG"; then + # Extract the first word of "pkg-config", so it can be a program name with args. +set dummy pkg-config; ac_word=$2 +echo "$as_me:2026: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_path_PKG_CONFIG+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) + ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. + ;; + *) + ac_save_IFS=$IFS; IFS=$ac_path_separator +ac_dummy="$PATH" +for ac_dir in $ac_dummy; do + IFS=$ac_save_IFS + test -z "$ac_dir" && ac_dir=. + if $as_executable_p "$ac_dir/$ac_word"; then + ac_cv_path_PKG_CONFIG="$ac_dir/$ac_word" + echo "$as_me:2043: found $ac_dir/$ac_word" >&5 + break +fi +done + + test -z "$ac_cv_path_PKG_CONFIG" && ac_cv_path_PKG_CONFIG="no" + ;; +esac +fi +PKG_CONFIG=$ac_cv_path_PKG_CONFIG + +if test -n "$PKG_CONFIG"; then + echo "$as_me:2055: result: $PKG_CONFIG" >&5 +echo "${ECHO_T}$PKG_CONFIG" >&6 +else + echo "$as_me:2058: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + fi + + if test "$PKG_CONFIG" = "no" ; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + echo "$as_me:2072: checking for jack" >&5 +echo $ECHO_N "checking for jack... $ECHO_C" >&6 + + if $PKG_CONFIG --exists "jack" ; then + echo "$as_me:2076: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + succeeded=yes + + echo "$as_me:2080: checking JACK_CFLAGS" >&5 +echo $ECHO_N "checking JACK_CFLAGS... $ECHO_C" >&6 + JACK_CFLAGS=`$PKG_CONFIG --cflags "jack"` + echo "$as_me:2083: result: $JACK_CFLAGS" >&5 +echo "${ECHO_T}$JACK_CFLAGS" >&6 + + echo "$as_me:2086: checking JACK_LIBS" >&5 +echo $ECHO_N "checking JACK_LIBS... $ECHO_C" >&6 + JACK_LIBS=`$PKG_CONFIG --libs "jack"` + echo "$as_me:2089: result: $JACK_LIBS" >&5 +echo "${ECHO_T}$JACK_LIBS" >&6 + else + JACK_CFLAGS="" + JACK_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + JACK_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "jack"` + + fi + + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + have_jack=yes + else + have_jack=no + fi + +# Check whether --with-alsa or --without-alsa was given. +if test "${with_alsa+set}" = set; then + withval="$with_alsa" + with_alsa=$withval +else + with_alsa="yes" +fi; + +# Check whether --with-jack or --without-jack was given. +if test "${with_jack+set}" = set; then + withval="$with_jack" + with_jack=$withval +else + with_jack="yes" +fi; + +# Check whether --with-oss or --without-oss was given. +if test "${with_oss+set}" = set; then + withval="$with_oss" + with_oss=$withval +else + with_oss="yes" +fi; + +CFLAGS="-g -O2 -Wall" + +if [ $ac_cv_c_bigendian = "yes" ] ; then + CFLAGS="$CFLAGS -DPA_BIG_ENDIAN" +else + CFLAGS="$CFLAGS -DPA_LITTLE_ENDIAN" +fi + +case "${host_os}" in + darwin* ) + + OTHER_OBJS="pa_mac_core/pa_mac_core.o"; + LIBS="-framework AudioUnit -framework AudioToolbox -framework CoreAudio"; + PADLL="libportaudio.dylib"; + SHARED_FLAGS="-framework AudioUnit -framework AudioToolbox"; + SHARED_FLAGS="$SHARED_FLAGS -framework CoreAudio -dynamiclib"; + ;; + + mingw* ) + + OTHER_OBJS="pa_win_wmme/pa_win_wmme.o"; + LIBS="-lwinmm -lm"; + PADLL="portaudio.dll"; + SHARED_FLAGS="-shared -mthreads"; + DLL_LIBS="-lwinmm"; + ;; + + cygwin* ) + + OTHER_OBJS="pa_win_wmme/pa_win_wmme.o"; + LIBS="-lwinmm -lm"; + PADLL="portaudio.dll"; + SHARED_FLAGS="-shared -mthreads"; + DLL_LIBS="-lwinmm"; + ;; + + *) + +echo "$as_me:2174: checking for pthread_create in -lpthread" >&5 +echo $ECHO_N "checking for pthread_create in -lpthread... $ECHO_C" >&6 +if test "${ac_cv_lib_pthread_pthread_create+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat >conftest.$ac_ext <<_ACEOF +#line 2182 "configure" +#include "confdefs.h" + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_create (); +int +main () +{ +pthread_create (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:2201: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:2204: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:2207: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:2210: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pthread_pthread_create=yes +else + echo "$as_me: failed program was:" >&5 +cat conftest.$ac_ext >&5 +ac_cv_lib_pthread_pthread_create=no +fi +rm -f conftest.$ac_objext conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:2221: result: $ac_cv_lib_pthread_pthread_create" >&5 +echo "${ECHO_T}$ac_cv_lib_pthread_pthread_create" >&6 +if test $ac_cv_lib_pthread_pthread_create = yes; then + cat >>confdefs.h <&5 +echo "$as_me: error: libpthread not found!" >&2;} + { (exit 1); exit 1; }; } +fi + + if [ $have_alsa = "yes" ] && [ $with_alsa != "no" ] ; then + LIBS="$LIBS -lasound" + OTHER_OBJS="$OTHER_OBJS pa_linux_alsa/pa_linux_alsa.o" + OTHER_OBJS="$OTHER_OBJS pa_linux_alsa/callback_thread.o" + OTHER_OBJS="$OTHER_OBJS pa_linux_alsa/blocking_calls.o" + cat >>confdefs.h <<\EOF +#define PA_USE_ALSA 1 +EOF + + fi + + if [ $have_jack = "yes" ] && [ $with_jack != "no" ] ; then + LIBS="$LIBS $JACK_LIBS" + CFLAGS="$CFLAGS $JACK_CFLAGS" + OTHER_OBJS="$OTHER_OBJS pa_jack/pa_jack.o" + cat >>confdefs.h <<\EOF +#define PA_USE_JACK 1 +EOF + + fi + + if [ $with_oss != "no" ] ; then + OTHER_OBJS="$OTHER_OBJS pa_unix_oss/pa_unix_oss.o" + cat >>confdefs.h <<\EOF +#define PA_USE_OSS 1 +EOF + + fi + LIBS="$LIBS -lm -lpthread"; + PADLL="libportaudio.so"; + SHARED_FLAGS="-shared"; + + OTHER_OBJS="$OTHER_OBJS pa_unix/pa_unix_hostapis.o pa_unix/pa_unix_util.o" +esac + +ac_config_files="$ac_config_files Makefile" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overriden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if cmp -s $cache_file confcache; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\EOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +EOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:2381: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +SHELL=\${CONFIG_SHELL-$SHELL} +ac_cs_invocation="\$0 \$@" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi + +# Name of the executable. +as_me=`echo "$0" |sed 's,.*[\\/],,'` + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +as_executable_p="test -f" + +# Support unset when possible. +if (FOO=FOO; unset FOO) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + +# NLS nuisances. +$as_unset LANG || test "${LANG+set}" != set || { LANG=C; export LANG; } +$as_unset LC_ALL || test "${LC_ALL+set}" != set || { LC_ALL=C; export LC_ALL; } +$as_unset LC_TIME || test "${LC_TIME+set}" != set || { LC_TIME=C; export LC_TIME; } +$as_unset LC_CTYPE || test "${LC_CTYPE+set}" != set || { LC_CTYPE=C; export LC_CTYPE; } +$as_unset LANGUAGE || test "${LANGUAGE+set}" != set || { LANGUAGE=C; export LANGUAGE; } +$as_unset LC_COLLATE || test "${LC_COLLATE+set}" != set || { LC_COLLATE=C; export LC_COLLATE; } +$as_unset LC_NUMERIC || test "${LC_NUMERIC+set}" != set || { LC_NUMERIC=C; export LC_NUMERIC; } +$as_unset LC_MESSAGES || test "${LC_MESSAGES+set}" != set || { LC_MESSAGES=C; export LC_MESSAGES; } + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH || test "${CDPATH+set}" != set || { CDPATH=:; export CDPATH; } + +exec 6>&1 + +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\EOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to ." +EOF + +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + shift + set dummy "$ac_option" "$ac_optarg" ${1+"$@"} + shift + ;; + -*);; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_need_defaults=false;; + esac + + case $1 in + # Handling of the options. +EOF +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:2549: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + shift + CONFIG_FILES="$CONFIG_FILES $1" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + shift + CONFIG_HEADERS="$CONFIG_HEADERS $1" + ac_need_defaults=false;; + + # This is an error. + -*) { { echo "$as_me:2568: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +exec 5>>config.log +cat >&5 << _ACEOF + +## ----------------------- ## +## Running config.status. ## +## ----------------------- ## + +This file was extended by $as_me 2.52, executed with + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + > $ac_cs_invocation +on `(hostname || uname -n) 2>/dev/null | sed 1q` + +_ACEOF +EOF + +cat >>$CONFIG_STATUS <<\EOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + *) { { echo "$as_me:2604: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. +: ${TMPDIR=/tmp} +{ + tmp=`(umask 077 && mktemp -d -q "$TMPDIR/csXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=$TMPDIR/cs$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in $TMPDIR" >&2 + { (exit 1); exit 1; } +} + +EOF + +cat >>$CONFIG_STATUS <\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@DEFS@,$DEFS,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@AR@,$AR,;t t +s,@OTHER_OBJS@,$OTHER_OBJS,;t t +s,@PADLL@,$PADLL,;t t +s,@SHARED_FLAGS@,$SHARED_FLAGS,;t t +s,@DLL_LIBS@,$DLL_LIBS,;t t +s,@PKG_CONFIG@,$PKG_CONFIG,;t t +s,@JACK_CFLAGS@,$JACK_CFLAGS,;t t +s,@JACK_LIBS@,$JACK_LIBS,;t t +CEOF + +EOF + + cat >>$CONFIG_STATUS <<\EOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +EOF +cat >>$CONFIG_STATUS <<\EOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + { case "$ac_dir" in + [\\/]* | ?:[\\/]* ) as_incr_dir=;; + *) as_incr_dir=.;; +esac +as_dummy="$ac_dir" +for as_mkdir_dir in `IFS='/\\'; set X $as_dummy; shift; echo "$@"`; do + case $as_mkdir_dir in + # Skip DOS drivespec + ?:) as_incr_dir=$as_mkdir_dir ;; + *) + as_incr_dir=$as_incr_dir/$as_mkdir_dir + test -d "$as_incr_dir" || mkdir "$as_incr_dir" + ;; + esac +done; } + + ac_dir_suffix="/`echo $ac_dir|sed 's,^\./,,'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo "$ac_dir_suffix" | sed 's,/[^/]*,../,g'` + else + ac_dir_suffix= ac_dots= + fi + + case $srcdir in + .) ac_srcdir=. + if test -z "$ac_dots"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_dots | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_dots$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_dots$srcdir ;; + esac + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_dots$INSTALL ;; + esac + + if test x"$ac_file" != x-; then + { echo "$as_me:2818: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated automatically by config.status. */ + configure_input="Generated automatically from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:2836: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo $f;; + *) # Relative + if test -f "$f"; then + # Build tree + echo $f + elif test -f "$srcdir/$f"; then + # Source tree + echo $srcdir/$f + else + # /dev/null tree + { { echo "$as_me:2849: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +EOF +cat >>$CONFIG_STATUS <>$CONFIG_STATUS <<\EOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +EOF + +cat >>$CONFIG_STATUS <<\EOF + +{ (exit 0); exit 0; } +EOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + exec 5>/dev/null + $SHELL $CONFIG_STATUS || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/pd/portaudio/configure.in b/pd/portaudio/configure.in new file mode 100644 index 00000000..edb0fe11 --- /dev/null +++ b/pd/portaudio/configure.in @@ -0,0 +1,123 @@ +dnl +dnl portaudio V19 configure.in script +dnl +dnl Dominic Mazzoni +dnl + +dnl Require autoconf >= 2.13 +AC_PREREQ(2.13) + +dnl Init autoconf and make sure configure is being called +dnl from the right directory +AC_INIT([pa_common/portaudio.h]) + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_RANLIB +AC_PROG_INSTALL +AC_PATH_PROG(AR, ar, no) +if [[ $AR = "no" ]] ; then + AC_MSG_ERROR("Could not find ar - needed to create a library"); +fi + +dnl This must be one of the first tests we do or it will fail... +AC_C_BIGENDIAN + +dnl extra variables +AC_SUBST(OTHER_OBJS) +AC_SUBST(PADLL) +AC_SUBST(SHARED_FLAGS) +AC_SUBST(DLL_LIBS) + +dnl checks for various host APIs and arguments to configure that +dnl turn them on or off + +AC_CHECK_LIB(asound, snd_pcm_open, have_alsa=yes, have_alsa=no) + +PKG_CHECK_MODULES(JACK, jack, have_jack=yes, have_jack=no) + +AC_ARG_WITH(alsa, + [ --with-alsa (default=auto)], + with_alsa=$withval, with_alsa="yes") + +AC_ARG_WITH(jack, + [ --with-jack (default=auto)], + with_jack=$withval, with_jack="yes") + +AC_ARG_WITH(oss, + [ --with-oss (default=yes)], + with_oss=$withval, with_oss="yes") + +CFLAGS="-g -O2 -Wall" + +if [[ $ac_cv_c_bigendian = "yes" ]] ; then + CFLAGS="$CFLAGS -DPA_BIG_ENDIAN" +else + CFLAGS="$CFLAGS -DPA_LITTLE_ENDIAN" +fi + +case "${host_os}" in + darwin* ) + dnl Mac OS X configuration + + OTHER_OBJS="pa_mac_core/pa_mac_core.o"; + LIBS="-framework AudioUnit -framework AudioToolbox -framework CoreAudio"; + PADLL="libportaudio.dylib"; + SHARED_FLAGS="-framework AudioUnit -framework AudioToolbox"; + SHARED_FLAGS="$SHARED_FLAGS -framework CoreAudio -dynamiclib"; + ;; + + mingw* ) + dnl MingW configuration + + OTHER_OBJS="pa_win_wmme/pa_win_wmme.o"; + LIBS="-lwinmm -lm"; + PADLL="portaudio.dll"; + SHARED_FLAGS="-shared -mthreads"; + DLL_LIBS="-lwinmm"; + ;; + + cygwin* ) + dnl Cygwin configuration + + OTHER_OBJS="pa_win_wmme/pa_win_wmme.o"; + LIBS="-lwinmm -lm"; + PADLL="portaudio.dll"; + SHARED_FLAGS="-shared -mthreads"; + DLL_LIBS="-lwinmm"; + ;; + + *) + dnl Unix OSS configuration + + AC_CHECK_LIB(pthread, pthread_create, + , + AC_MSG_ERROR([libpthread not found!])) + + if [[ $have_alsa = "yes" ] && [ $with_alsa != "no" ]] ; then + LIBS="$LIBS -lasound" + OTHER_OBJS="$OTHER_OBJS pa_linux_alsa/pa_linux_alsa.o" + OTHER_OBJS="$OTHER_OBJS pa_linux_alsa/callback_thread.o" + OTHER_OBJS="$OTHER_OBJS pa_linux_alsa/blocking_calls.o" + AC_DEFINE(PA_USE_ALSA) + fi + + if [[ $have_jack = "yes" ] && [ $with_jack != "no" ]] ; then + LIBS="$LIBS $JACK_LIBS" + CFLAGS="$CFLAGS $JACK_CFLAGS" + OTHER_OBJS="$OTHER_OBJS pa_jack/pa_jack.o" + AC_DEFINE(PA_USE_JACK) + fi + + if [[ $with_oss != "no" ]] ; then + OTHER_OBJS="$OTHER_OBJS pa_unix_oss/pa_unix_oss.o" + AC_DEFINE(PA_USE_OSS) + fi + LIBS="$LIBS -lm -lpthread"; + PADLL="libportaudio.so"; + SHARED_FLAGS="-shared"; + + OTHER_OBJS="$OTHER_OBJS pa_unix/pa_unix_hostapis.o pa_unix/pa_unix_util.o" +esac + +AC_OUTPUT([Makefile]) diff --git a/pd/portaudio/docs/index.html b/pd/portaudio/docs/index.html new file mode 100644 index 00000000..7d9b248d --- /dev/null +++ b/pd/portaudio/docs/index.html @@ -0,0 +1,60 @@ + + + + + + + + + PortAudio Docs + + +  +
+ + + +
+
+

+PortAudio Documentation

+
+ +

Copyright 2000 Phil Burk and Ross Bencina +
  +

+API Reference

+ +
The Application Programmer Interface is documented in "portaudio.h".
+ +

+Tutorial

+ +
Describes how to write audio programs using the PortAudio API.
+ +

+Implementation Guide

+ +
Describes how to write an implementation of PortAudio for a +new computer platform.
+ +

+Paper Presented at ICMC2001 (PDF)

+ +
Describes the PortAudio API and discusses implementation issues. +Written July 2001.
+ +

+Improving Latency

+ +
How to tune your computer to achieve the lowest possible audio +delay.
+ +

+Proposed Changes

+ +
Describes API changes being considered by the developer community. +Feedback welcome.
+Return to PortAudio Home Page + + diff --git a/pd/portaudio/docs/latency.html b/pd/portaudio/docs/latency.html new file mode 100644 index 00000000..87f1d122 --- /dev/null +++ b/pd/portaudio/docs/latency.html @@ -0,0 +1,192 @@ + + + + + + + + + PortAudio Implementation - Start/Stop + + +  +
+ + + +
+
+

+PortAudio Latency

+
+ +

This page discusses the issues of audio latency for PortAudio +. It offers suggestions on how to lower latency to improve the responsiveness +of applications. +

What is Latency? +
PortAudio and Latency +
Macintosh +
Unix +
WIndows
+By Phil Burk, Copyright 2002 Phil Burk and Ross Bencina +

+What is Latency?

+Latency is basically longest time that you have to wait before you obtain +a desired result. For digital audio output it is the time between making +a sound in software and finally hearing it. +

Consider the example of pressing a key on the ASCII keyboard to play +a note. There are several stages in this process which each contribute +their own latency. First the operating system must respond to the keypress. +Then the audio signal generated must work its way through the PortAudio +buffers. Then it must work its way through the audio card hardware. Then +it must go through the audio amplifier which is very quick and then travel +through the air. Sound travels at abous one foot per millisecond through +air so placing speakers across the room can add 5-20 msec of delay. +

The reverse process occurs when recording or responding to audio input. +If you are processing audio, for example if you implement a software guitar +fuzz box, then you have both the audio input and audio output latencies +added together. +

The audio buffers are used to prevent glitches in the audio stream. +The user software writes audio into the output buffers. That audio is read +by the low level audio driver or by DMA and sent to the DAC. If the computer +gets busy doing something like reading the disk or redrawing the screen, +then it may not have time to fill the audio buffer. The audio hardware +then runs out of audio data, which causes a glitch. By using a large enough +buffer we can ensure that there is always enough audio data for the audio +hardware to play. But if the buffer is too large then the latency is high +and the system feels sluggish. If you play notes on the keyboard then the +"instrument" will feel unresponsive. So you want the buffers to be as small +as possible without glitching. +

+PortAudio and Latency

+The only delay that PortAudio can control is the total length of its buffers. +The Pa_OpenStream() call takes two parameters: numBuffers and framesPerBuffer. +The latency is also affected by the sample rate which we will call framesPerSecond. +A frame is a set of samples that occur simultaneously. For a stereo stream, +a frame is two samples. +

The latency in milliseconds due to this buffering  is: +

latency_msec = 1000 * numBuffers * framesPerBuffer / framesPerSecond
+This is not the total latency, as we have seen, but it is the part we can +control. +

If you call Pa_OpenStream() with numBuffers equal to zero, then PortAudio +will select a conservative number that will prevent audio glitches. If +you still get glitches, then you can pass a larger value for numBuffers +until the glitching stops. if you try to pass a numBuffers value that is +too small, then PortAudio will use its own idea of the minimum value. +

PortAudio decides on the minimum number of buffers in a conservative +way based on the frameRate, operating system and other variables. You can +query the value that PortAudio will use by calling: +

int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate +);
+On some systems you can override the PortAudio minimum if you know your +system can handle a lower value. You do this by setting an environment +variable called PA_MIN_LATENCY_MSEC which is read by PortAudio when it +starts up. This is supported on the PortAudio implementations for Windows +MME, Windows DirectSound, and Unix OSS. +

+Macintosh

+The best thing you can do to improve latency on Mac OS 8 and 9 is to turn +off Virtual Memory. PortAudio V18 will detect that Virtual Memory is turned +off and use a very low latency. +

For Mac OS X the latency is very low because Apple Core Audio is so +well written. You can set the PA_MIN_LATENCY_MSEC variable using: +

setenv PA_MIN_LATENCY_MSEC 4
+ +

+Unix

+PortAudio under Unix currently uses a backgroud thread that reads and writes +to OSS. This gives you decent but not great latency. But if you raise the +priority of the background thread to a very priority then you can get under +10 milliseconds latency. In order to raise your priority you must run the +PortAudio program as root! You must also set PA_MIN_LATENCY_MSEC using +the appropriate command for your shell. +

+Windows

+Latency under Windows is a complex issue because of all the alternative +operating system versions and device drivers. I have seen latency range +from 8 milliseconds to 400 milliseconds. The worst case is when using Windows +NT. Windows 98 is a little better, and Windows XP can be quite good if +properly tuned. +

The underlying audio API also makes a lot of difference. If the audio +device has its own DirectSound driver then DirectSound can often provide +better latency than WMME. But if a real DirectSound driver is not available +for your device then it is emulated using WMME and the latency can be very +high. That's where I saw the 400 millisecond latency. The ASIO implementation +is generally very good and will give the lowest latency if available. +

You can set the PA_MIN_LATENCY_MSEC variable to 50, for example, by +entering in MS-DOS: +

set PA_MIN_LATENCY_MSEC=50
+If you enter this in a DOS window then you must run the PortAudio program +from that same window for the variable to have an effect. You can add that +line to your C:\AUTOEXEC.BAT file and reboot if you want it to affect any +PortAudio based program. +

For Windows XP, you can set environment variables as follows: +

    +
  1. +Select "Control Panel" from the "Start Menu".
  2. + +
  3. +Launch the "System" Control Panel
  4. + +
  5. +Click on the "Advanced" tab.
  6. + +
  7. +Click on the "Environment Variables" button.
  8. + +
  9. +Click "New" button under  User Variables.
  10. + +
  11. +Enter PA_MIN_LATENCY_MSEC for the name and some optimistic number for the +value.
  12. + +
  13. +Click OK, OK, OK.
  14. +
+ +

+Improving Latency on Windows

+There are several steps you can take to improve latency under windows. +
    +
  1. +Avoid reading or writng to disk when doing audio.
  2. + +
  3. +Turn off all automated background tasks such as email clients, virus scanners, +backup programs, FTP servers, web servers, etc. when doing audio.
  4. + +
  5. +Disconnect from the network to prevent network traffic from interrupting +your CPU.
  6. +
+Important: Windows XP users can also tune the OS to favor background +tasks, such as audio, over foreground tasks, such as word processing. I +lowered my latency from 40 to 10 milliseconds using this simple technique. +
    +
  1. +Select "Control Panel" from the "Start Menu".
  2. + +
  3. +Launch the "System" Control Panel
  4. + +
  5. +Click on the "Advanced" tab.
  6. + +
  7. +Click on the "Settings" button in the Performance area.
  8. + +
  9. +Click on the "Advanced" tab.
  10. + +
  11. +Select "Background services" in the Processor Scheduling area.
  12. + +
  13. +Click OK, OK.
  14. +
+Please let us know if you have others sugestions for lowering latency. +
  +
  + + diff --git a/pd/portaudio/docs/pa_impl_guide.html b/pd/portaudio/docs/pa_impl_guide.html new file mode 100644 index 00000000..50abc304 --- /dev/null +++ b/pd/portaudio/docs/pa_impl_guide.html @@ -0,0 +1,197 @@ + + + + + + + + + PortAudio Implementation - Start/Stop + + +  +
+ + + +
+
+

+PortAudio Implementation Guide

+
+ +

This document describes how to implement the PortAudio API on a new +computer platform. Implementing PortAudio on a new platform, makes it possible +to port many existing audio applications to that platform. +

By Phil Burk +
Copyright 2000 Phil Burk and Ross Bencina +

Note that the license says: "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.". +So when you have finished a new implementation, please send it back to +us at  "http://www.portaudio.com" +so that we can make it available for other users. Thank you! +

+Download the Latest PortAudio Implementation

+Always start with the latest implementation available at "http://www.portaudio.com". +Look for the nightly snapshot under the CVS section. +

+Select an Existing Implementation as a Basis

+The fastest way to get started is to take an existing implementation and +translate it for your new platform. Choose an implementation whose architecture +is as close as possible to your target. +
    +
  • +DirectSound Implementation - pa_win_ds - Uses a timer callback for the +background "thread". Polls a circular buffer and writes blocks of data +to keep it full.
  • + +
  • +Windows MME - pa_win_wmme - Spawns an actual Win32 thread. Writes blocks +of data to the HW device and waits for events that signal buffer completion.
  • + +
  • +Linux OSS - pa_linux - Spawns a real thread that writes to the "/dev/dsp" +stream using blocking I/O calls.
  • +
+When you write a new implementation, you will be using some code that is +in common with all implementations. This code is in the folder "pa_common". +It provides various functions such as parameter checking, error code to +text conversion, sample format conversion, clipping and dithering, etc. +

The code that you write will go into a separate folder called "pa_{os}_{api}". +For example, code specific to the DirectSound interface for Windows goes +in "pa_win_ds". +

+Read Docs and Code

+Famialiarize yourself with the system by reading the documentation provided. +here is a suggested order: +
    +
  1. +User Programming Tutorial
  2. + +
  3. +Header file "pa_common/portaudio.h" which defines API.
  4. + +
  5. +Header file "pa_common/pa_host.h" for host dependant code. This definces +the routine you will need to provide.
  6. + +
  7. +Shared code in "pa_common/pa_lib.c".
  8. + +
  9. +Docs on Implementation of Start/Stop +code.
  10. +
+ +

+Implement  Output to Default Device

+Now we are ready to crank some code. For instant gratification, let's try +to play a sine wave. +
    +
  1. +Link the test program "pa_tests/patest_sine.c" with the file "pa_lib.c" +and the implementation specific file you are creating.
  2. + +
  3. +For now, just stub out the device query code and the audio input code.
  4. + +
  5. +Modify PaHost_OpenStream() to open your default target device and get everything +setup.
  6. + +
  7. +Modify PaHost_StartOutput() to start playing audio.
  8. + +
  9. +Modify PaHost_StopOutput() to stop audio.
  10. + +
  11. +Modify PaHost_CloseStream() to clean up. Free all memory that you allocated +in PaHost_OpenStream().
  12. + +
  13. +Keep cranking until you can play a sine wave using "patest_sine.c".
  14. + +
  15. +Once that works, try "patest_pink.c", "patest_clip.c", "patest_sine8.c".
  16. + +
  17. +To test your Open and Close code, try "patest_many.c".
  18. + +
  19. +Now test to make sure that the three modes of stopping are properly supported +by running "patest_stop.c".
  20. + +
  21. +Test your implementation of time stamping with "patest_sync.c".
  22. +
+ +

+Implement Device Queries

+Now that output is working, lets implement the code for querying what devices +are available to the user. Run "pa_tests/pa_devs.c". It should print all +of the devices available and their characteristics. +

+Implement Input

+Implement audio input and test it with: +
    +
  1. +patest_record.c - record in half duplex, play back as recorded.
  2. + +
  3. +patest_wire.c - full duplex, copies input to output. Note that some HW +may not support full duplex.
  4. + +
  5. +patest_fuzz.c - plug in your guitar and get a feel for why latency is an +important issue in computer music.
  6. + +
  7. +paqa_devs.c - try to open every device and use it with every possible format
  8. +
+ +

+Debugging Tools

+You generally cannot use printf() calls to debug real-time processes because +they disturb the timing. Also calling printf() from your background thread +or interrupt could crash the machine. So PA includes a tool for capturing +events and storing the information while it is running. It then prints +the events when Pa_Terminate() is called. +
    +
  1. +To enable trace mode, change TRACE_REALTIME_EVENTS in "pa_common/pa_trace.h" +from a (0) to a (1).
  2. + +
  3. +Link with "pa_common/pa_trace.c".
  4. + +
  5. +Add trace messages to your code by calling:
  6. + +
       void AddTraceMessage( char *msg, int data ); +
    for example +
       AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", +past->past_NumCallbacks ); +
  7. +Run your program. You will get a dump of events at the end.
  8. + +
  9. +You can leave the trace messages in your code. They will turn to NOOPs +when you change TRACE_REALTIME_EVENTS back to (0).
  10. +
+ +

+Delivery

+Please send your new code along with notes on the implementation back to +us at "http://www.portaudio.com". +We will review the implementation and post it with your name. If you had +to make any modifications to the code in "pa_common" or "pa_tests" please +send us those modifications and your notes. We will try to merge your changes +so that the "pa_common" code works with all implementations. +

If you have suggestions for how to make future implementations easier, +please let us know. +
THANKS! +
  + + diff --git a/pd/portaudio/docs/pa_impl_startstop.html b/pd/portaudio/docs/pa_impl_startstop.html new file mode 100644 index 00000000..0f2d0ce5 --- /dev/null +++ b/pd/portaudio/docs/pa_impl_startstop.html @@ -0,0 +1,190 @@ + + + + + + + + + PortAudio Implementation - Start/Stop + + +  +

+ + + +
+
+

+PortAudio Implementation

+
+ +

+Starting and Stopping Streams

+PortAudio is generally executed in two "threads". The foreground thread +is the application thread. The background "thread" may be implemented as +an actual thread, an interrupt handler, or a callback from a timer thread. +

There are three ways that PortAudio can stop a stream. In each case +we look at the sequence of events and the messages sent between the two +threads. The following variables are contained in the internalPortAudioStream. +

int   past_IsActive;     +/* Background is still playing. */ +
int   past_StopSoon;     /* Stop +when last buffer done. */ +
int   past_StopNow;      /* +Stop IMMEDIATELY. */
+ +

+Pa_AbortStream()

+This function causes the background thread to terminate as soon as possible +and audio I/O to stop abruptly. +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Foreground ThreadBackground Thread
sets StopNow
sees StopNow
clears IsActive, stops thread
waits for thread to exit
turns off audio I/O
+ +

+Pa_StopStream()

+This function stops the user callback function from being called and then +waits for all audio data written to the output buffer to be played. In +a system with very low latency, you may not hear any difference between +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Foreground ThreadBackground Thread
sets StopSoon
stops calling user callback
continues until output buffer empty
clears IsActive, stops thread
waits for thread to exit
turns off audio I/O
+ +

+User callback returns one.

+If the user callback returns one then the user callback function will no +longer be called. Audio output will continue until all audio data written +to the output buffer has been played. Then the audio I/O is stopped, the +background thread terminates, and the stream becomes inactive. +
  + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Foreground ThreadBackground Thread
callback returns 1
sets StopSoon
stops calling user callback
continues until output buffer empty
clears IsActive, stops thread
waits for thread to exit
turns off audio I/O
+ +
  + + diff --git a/pd/portaudio/docs/pa_tut_asio.html b/pd/portaudio/docs/pa_tut_asio.html new file mode 100644 index 00000000..1c3e5bfa --- /dev/null +++ b/pd/portaudio/docs/pa_tut_asio.html @@ -0,0 +1,55 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for ASIO (Windows or Macintosh)

+ +
ASIO is a low latency audio API from Steinberg. To compile +an ASIO application, you must first download +the ASIO SDK from Steinberg. You also need to obtain ASIO drivers for +your audio device. +

Note: I am using '/' as a file separator below. On Macintosh replace +'/' with ':'. On Windows, replace '/' with '\'. +

 To use ASIO with the PortAudio library add the following source +files to your project: +

+
pa_asio/pa_asio.cpp
+
+and also these files from the ASIO SDK: +
+
common/asio.cpp
+host/asiodrivers.cpp
+host/asiolist.cpp
+
+and add these directories to the path for include files +
+
asiosdk2/host/pc   (for Windows)
+asiosdk2/common
+asiosdk2/host
+
+You may try compiling the "pa_tests/patest_saw.c" file first because it +is the simplest.
+home | +contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_callback.html b/pd/portaudio/docs/pa_tut_callback.html new file mode 100644 index 00000000..f5ccaf0f --- /dev/null +++ b/pd/portaudio/docs/pa_tut_callback.html @@ -0,0 +1,91 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Writing a Callback Function

+ +
To write a program using PortAudio, you must include the "portaudio.h" +include file. You may wish to read "portaudio.h" +because it contains a complete description of the PortAudio functions and +constants. +
+
#include "portaudio.h"
+
+The next task is to write your custom callback function. It is a function +that is called by the PortAudio engine whenever it has captured audio data, +or when it needs more audio data for output. +

Your callback function is often called by an interrupt, or low level +process so you should not do any complex system activities like allocating +memory, or reading or writing files, or printf(). Just crunch numbers and +generate audio signals. What is safe or not safe will vary from platform +to platform. On the Macintosh, for example, you can only call "interrupt +safe" routines. Also do not call any PortAudio functions in the callback +except for Pa_StreamTime() and Pa_GetCPULoad(). +

Your callback function must return an int and accept the exact parameters +specified in this typedef: +

+
typedef int (PortAudioCallback)(
+               void *inputBuffer, void *outputBuffer,
+               unsigned long framesPerBuffer,
+               PaTimestamp outTime, void *userData );
+
+Here is an example callback function from the test file "patests/patest_saw.c". +It calculates a simple left and right sawtooth signal and writes it to +the output buffer. Notice that in this example, the signals are of float +data type. The signals must be between -1.0 and +1.0. You can also use +16 bit integers or other formats which are specified during setup. You +can pass a pointer to your data structure through PortAudio which will +appear as userData. +
+
int patestCallback(  void *inputBuffer, void *outputBuffer,
+                     unsigned long framesPerBuffer,
+                     PaTimestamp outTime, void *userData )
+{
+    unsigned int i;
+/* Cast data passed through stream to our structure type. */
+    paTestData *data = (paTestData*)userData;
+    float *out = (float*)outputBuffer;
+        
+    for( i=0; i<framesPerBuffer; i++ )
+    {
+    /* Stereo channels are interleaved. */
+        *out++ = data->left_phase;              /* left */
+        *out++ = data->right_phase;             /* right */
+
+    /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */
+        data->left_phase += 0.01f;
+    /* When signal reaches top, drop back down. */
+        if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f;
+
+    /* higher pitch so we can distinguish left and right. */
+        data->right_phase += 0.03f; 
+        if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f;
+    }
+    return 0;
+}
+
+
+home | +contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_devs.html b/pd/portaudio/docs/pa_tut_devs.html new file mode 100644 index 00000000..1756992c --- /dev/null +++ b/pd/portaudio/docs/pa_tut_devs.html @@ -0,0 +1,65 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Querying for Available Devices

+ +
There are often several different audio devices available in +a computer with different capabilities. They can differ in the sample rates +supported, bit widths, etc. PortAudio provides a simple way to query for +the available devices, and then pass the selected device to Pa_OpenStream(). +For an example, see the file "pa_tests/pa_devs.c". +

To determine the number of devices: +

+
numDevices = Pa_CountDevices();
+
+You can then query each device in turn by calling Pa_GetDeviceInfo() with +an index. +
+
for( i=0; i<numDevices; i++ ) {
+     pdi = Pa_GetDeviceInfo( i );
+
+It will return a pointer to a PaDeviceInfo structure which is +defined as: +
+
typedef struct{
+    int structVersion; 
+    const char *name;
+    int maxInputChannels;
+    int maxOutputChannels;
+/* Number of discrete rates, or -1 if range supported. */
+    int numSampleRates;
+/* Array of supported sample rates, or {min,max} if range supported. */
+    const double *sampleRates;
+    PaSampleFormat nativeSampleFormat;
+}PaDeviceInfo;
+
+If the device supports a continuous range of sample rates, then numSampleRates +will equal -1, and the sampleRates array will have two values, the minimum  +and maximum rate. +

The device information is allocated by Pa_Initialize() and freed by +Pa_Terminate() so you do not have to free() the structure returned by Pa_GetDeviceInfo().

+home | +contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_explore.html b/pd/portaudio/docs/pa_tut_explore.html new file mode 100644 index 00000000..91c08a5b --- /dev/null +++ b/pd/portaudio/docs/pa_tut_explore.html @@ -0,0 +1,42 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Exploring PortAudio

+ +
Now that you have a good idea of how PortAudio works, you can +try out the test programs. +
    +
  • +For an example of playing a sine wave, see "pa_tests/patest_sine.c".
  • + +
  • +For an example of recording and playing back a sound, see  "pa_tests/patest_record.c".
  • +
+I also encourage you to examine the source for the PortAudio libraries. +If you have suggestions on ways to improve them, please let us know. if +you want to implement PortAudio on a new platform, please let us know as +well so we can coordinate people's efforts.
+home | contents +| previous |  next + + diff --git a/pd/portaudio/docs/pa_tut_init.html b/pd/portaudio/docs/pa_tut_init.html new file mode 100644 index 00000000..91bfa8d9 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_init.html @@ -0,0 +1,43 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Initializing PortAudio

+ +
Before making any other calls to PortAudio, you must call Pa_Initialize(). +This will trigger a scan of available devices which can be queried later. +Like most PA functions, it will return a result of type paError. +If the result is not paNoError, then an error has occurred. +
+
err = Pa_Initialize();
+if( err != paNoError ) goto error;
+
+You can get a text message that explains the error message by passing it +to +
+
printf(  "PortAudio error: %s\n", Pa_GetErrorText( err ) );
+
+
+home | contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_mac.html b/pd/portaudio/docs/pa_tut_mac.html new file mode 100644 index 00000000..bf3dafd1 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_mac.html @@ -0,0 +1,41 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for Macintosh

+ +
To compile a Macintosh application with the PortAudio library, +add the following source files to your project: +
+
pa_mac:pa_mac.c
+pa_common:pa_lib.c
+pa_common:portaudio.h
+pa_common:pa_host.h
+
+Also add the Apple SoundLib to your project. +

You may try compiling the "pa_tests:patest_saw.c" file first because +it is the simplest.

+home | +contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_mac_osx.html b/pd/portaudio/docs/pa_tut_mac_osx.html new file mode 100644 index 00000000..3af8c140 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_mac_osx.html @@ -0,0 +1,46 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for Macintosh OS X

+ +
To compile a Macintosh OS X CoreAudio application with the +PortAudio library: +

Create a new ProjectBuilder project. You can use a "Tool" project to +run the PortAudio examples. +

Add the following source files to your Project: +

+
pa_mac_core/pa_mac_core.c
+pa_common/pa_lib.c
+pa_common/portaudio.h
+pa_common/pa_host.h
+pa_common/pa_convert.c
+
+Add the Apple CoreAudio.framework to your project by selecting "Add FrameWorks..." +from the Project menu. +

Compile and run the "pa_tests:patest_saw.c" file first because it is +the simplest.

+home | +contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_open.html b/pd/portaudio/docs/pa_tut_open.html new file mode 100644 index 00000000..1621ef30 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_open.html @@ -0,0 +1,52 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Opening a Stream using Defaults

+ +
The next step is to open a stream which is similar to opening +a file. You can specify whether you want audio input and/or output, how +many channels, the data format, sample rate, etc. There are two calls for +opening streams, Pa_OpenStream() and Pa_OpenDefaultStream(). +

Pa_OpenStream() takes extra  parameters which give you +more control. You can normally just use Pa_OpenDefaultStream() +which just calls Pa_OpenStream() with some reasonable +default values.  Let's open a stream for stereo output, using floating +point data, at 44100 Hz. +

+
err = Pa_OpenDefaultStream(
+    &stream,        /* passes back stream pointer */
+    0,              /* no input channels */
+    2,              /* stereo output */
+    paFloat32,      /* 32 bit floating point output */
+    44100,          /* sample rate */
+    256,            /* frames per buffer */
+    0,              /* number of buffers, if zero then use default minimum */
+    patestCallback, /* specify our custom callback */
+    &data );        /* pass our data through to callback */
+
+If you want to use 16 bit integer data, pass paInt16 instead of +paFloat32.
+home | contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_oss.html b/pd/portaudio/docs/pa_tut_oss.html new file mode 100644 index 00000000..1bb76f25 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_oss.html @@ -0,0 +1,46 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for Unix OSS

+ +
[Skip this page if you are not using Unix and OSS] +

We currently support the OSS +audio drivers for Linux, Solaris, and FreeBSD. We hope to someday support +the newer ALSA drivers. +

    +
  1. +cd to pa_unix_oss directory
  2. + +
  3. +Edit the Makefile and uncomment one of the tests. You may try compiling +the "patest_sine.c" file first because it is very simple.
  4. + +
  5. +gmake run
  6. +
+
+home | +contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_over.html b/pd/portaudio/docs/pa_tut_over.html new file mode 100644 index 00000000..baa99205 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_over.html @@ -0,0 +1,92 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Overview of PortAudio

+ +
PortAudio is a library that provides streaming audio input +and output. It is a cross-platform API (Application Programming Interface) +that works on Windows, Macintosh, Unix running OSS, SGI, BeOS, and perhaps +other platforms by the time you read this. This means that you can write +a simple 'C' program to process or generate an audio signal, and that program +can run on several different types of computer just by recompiling the +source code. +

Here are the steps to writing a PortAudio application: +

    +
  1. +Write a callback function that will be called by PortAudio when audio processing +is needed.
  2. + +
  3. +Initialize the PA library and open a stream for audio I/O.
  4. + +
  5. +Start the stream. Your callback function will be now be called repeatedly +by PA in the background.
  6. + +
  7. +In your callback you can read audio data from the inputBuffer and/or write +data to the outputBuffer.
  8. + +
  9. +Stop the stream by returning 1 from your callback, or by calling a stop +function.
  10. + +
  11. +Close the stream and terminate the library.
  12. +
+
+ +
There is also another interface +provided that allows you to generate audio in the foreground. You then +simply write data to the stream and the tool will not return until it is +ready to accept more data. This interface is simpler to use but is usually +not preferred for large applications because it requires that you launch +a thread to perform the synthesis. Launching a thread may be difficult +on non-multi-tasking systems such as the Macintosh prior to MacOS X. +

Let's continue by building a simple application that will play a sawtooth +wave. +

Please select the page for the specific implementation you would like +to use: +

+or continue with the next page of the programming +tutorial.
+home | +contents +| previous + + diff --git a/pd/portaudio/docs/pa_tut_pc.html b/pd/portaudio/docs/pa_tut_pc.html new file mode 100644 index 00000000..87e5f9a0 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_pc.html @@ -0,0 +1,78 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Compiling for Windows (WMME or DirectSound)

+ +
To compile PortAudio for Windows, you can choose between two +options. One implementation uses the DirectSound API. The other uses the +Windows MultiMedia Extensions API (aka WMME or WAVE). +

Some advantages of using DirectSound are that DirectSound may have lower +latency than WMME, and supports effects processing plugins. But one disadvantage +is that DirectSound is not installed on all PCs, and is not well supported +under Windows NT. So WMME is the best choice for most projects. +

For either implementation add the following source files to your project: +

+
pa_common\pa_lib.c
+pa_common\portaudio.h
+pa_common\pa_host.h
+
+Link with the system library "winmm.lib". For Visual C++: +
    +
  1. +select "Settings..." from the "Project" menu,
  2. + +
  3. +select the project name in the tree on the left,
  4. + +
  5. +choose "All Configurations" in the popup menu above the tree,
  6. + +
  7. +select the "Link" tab,
  8. + +
  9. +enter "winmm.lib", without quotes, as the first item in the "Object/library +modules:" field.
  10. +
+WMME - To use the WMME implementation, add the following source +files to your project: +
pa_win_wmme/pa_win_wmme.c
+DirectSound - If you want to use the DirectSound implementation +of PortAudio then you must have a recent copy of the free +DirectX +SDK for Developers from Microsoft installed on your computer. To compile +an application add the following source files to your project: +
+
pa_win_ds\dsound_wrapper.c
+pa_win_ds\pa_dsound.c
+
+Link with the system library "dsound.lib" using the procedure described +above for "winmm.lib".
+ +
You might try compiling the "pa_tests\patest_saw.c" file first +because it is the simplest.
+home | +contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_run.html b/pd/portaudio/docs/pa_tut_run.html new file mode 100644 index 00000000..5c70d089 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_run.html @@ -0,0 +1,56 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Starting and Stopping a Stream

+ +
The stream will not start running until you call Pa_StartStream(). +Then it will start calling your callback function to perform the audio +processing. +
+
err = Pa_StartStream( stream );
+if( err != paNoError ) goto error;
+
+At this point, audio is being generated. You can communicate to your callback +routine through the data structure you passed in on the open call, or through +global variables, or using other interprocess communication techniques. +Please be aware that your callback function may be called at interrupt +time when your foreground process is least expecting it. So avoid sharing +complex data structures that are easily corrupted like double linked lists. +

In many of the tests we simply sleep for a few seconds so we can hear +the sound. This is easy to do with Pa_Sleep() which will sleep for some +number of milliseconds. Do not rely on this function for accurate scheduling. +it is mostly for writing examples. +

+
/* Sleep for several seconds. */
+Pa_Sleep(NUM_SECONDS*1000);
+
+When you are through, you can stop the stream from the foreground. +
+
err = Pa_StopStream( stream );
+if( err != paNoError ) goto error;
+
+You can also stop the stream by returning 1 from your custom callback function.
+home | contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_rw.html b/pd/portaudio/docs/pa_tut_rw.html new file mode 100644 index 00000000..93c7b8bb --- /dev/null +++ b/pd/portaudio/docs/pa_tut_rw.html @@ -0,0 +1,79 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Blocking Read/Write Functions

+ +
[Note: These functions are not part of the official PortAudio +API. They are simply built on top of PortAudio as an extra utility. Also +note that they are under evaluation and their definition may change.] +

There are two fundamentally different ways to design an audio API. One +is to use callback functions the way we have already shown. The callback +function operates under an interrupt or background thread This leaves the +foreground application free to do other things while the audio just runs +in the background. But this can sometimes be awkward. +

So we have provided an alternative technique that lets a program generate +audio in the foreground and then just write it to the audio stream as if +it was a file. If there is not enough room in the audio buffer for more +data, then the write function will just block until more room is available. +This can make it very easy to write an audio example. To use this tool, +you must add the files "pablio/pablio.c" and "pablio/ringbuffer.c" to your +project. You must also: +

+
#include "pablio.h"
+
+Here is a short excerpt of a program that opens a stream for input and +output. It then reads a block of samples from input, and writes them to +output, in a loop.  The complete example can be found in "pablio/test_rw.c". +
+
    #define SAMPLES_PER_FRAME     (2)
+    #define FRAMES_PER_BLOCK    (1024)
+    SAMPLE          samples[SAMPLES_PER_FRAME * FRAMES_PER_BLOCK];
+    PaError         err;
+    PABLIO_Stream  *aStream;
+
+/* Open simplified blocking I/O layer on top of PortAudio. */
+    err = OpenAudioStream( &rwbl, SAMPLE_RATE, paFloat32,
+                         (PABLIO_READ_WRITE | PABLIO_STEREO) );
+    if( err != paNoError ) goto error;
+
+/* Process samples in the foreground. */
+    for( i=0; i<(NUM_SECONDS * SAMPLE_RATE); i++ )
+    {
+    /* Read one block of data into sample array from audio input. */
+        ReadAudioStream( aStream, samples, FRAMES_PER_BLOCK );
+    /*
+    ** At this point you could process the data in samples array,
+    ** and write the result back to the same samples array.
+    */
+    /* Write that same frame of data to output. */
+        WriteAudioStream( aStream, samples, FRAMES_PER_BLOCK );
+    }
+
+    CloseAudioStream( aStream );
+
+
+home | +contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_term.html b/pd/portaudio/docs/pa_tut_term.html new file mode 100644 index 00000000..1c72209f --- /dev/null +++ b/pd/portaudio/docs/pa_tut_term.html @@ -0,0 +1,47 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Terminating PortAudio

+ +
You can start and stop a stream as many times as you like. +But when you are done using it, you should close it by calling:
+ +
+
+
err = Pa_CloseStream( stream );
+if( err != paNoError ) goto error;
+
+Then when you are done using PortAudio, you should terminate the whole +system by calling: +
+
Pa_Terminate();
+
+That's basically it. You can now write an audio program in 'C' that will +run on multiple platforms, for example PCs and Macintosh. +

In the rest of the tutorial we will look at some additional utility +functions, and a different way of using PortAudio that does not require +the use of a callback function.

+home | contents +| previousnext + + diff --git a/pd/portaudio/docs/pa_tut_util.html b/pd/portaudio/docs/pa_tut_util.html new file mode 100644 index 00000000..f4b54750 --- /dev/null +++ b/pd/portaudio/docs/pa_tut_util.html @@ -0,0 +1,55 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

+Utility Functions

+ +
Here are several more functions that are not critical, but +may be handy when using PortAudio. +

Pa_StreamActive() returns one when the stream in playing audio, zero +when not playing, or a negative error number if the stream is invalid. +The stream is active between calls to Pa_StartStream() and Pa_StopStream(), +but may also become inactive if the callback returns a non-zero value. +In the latter case, the stream is considered inactive after the last buffer +has finished playing. +

+
PaError Pa_StreamActive( PortAudioStream *stream );
+
+Pa_StreamTime() returns the number of samples that have been generated. +PaTimeStamp is a double precision number which is a convenient way to pass +big numbers around even though we only need integers. +
+
PaTimestamp Pa_StreamTime( PortAudioStream *stream );
+
+The "CPU Load" is a fraction of total CPU time consumed by the stream's +audio processing. A value of 0.5 would imply that PortAudio and the sound +generating callback was consuming roughly 50% of the available CPU time. +This function may be called from the callback function or the application. +
+
double Pa_GetCPULoad( PortAudioStream* stream );
+
+
+home | +contents | previous +|  next + + diff --git a/pd/portaudio/docs/pa_tutorial.html b/pd/portaudio/docs/pa_tutorial.html new file mode 100644 index 00000000..1371c44f --- /dev/null +++ b/pd/portaudio/docs/pa_tutorial.html @@ -0,0 +1,46 @@ + + + + + + + + + PortAudio Tutorial + + +  +
+ + + +
+
+

+PortAudio Tutorial

+
+ +

Copyright 2000 Phil Burk and Ross Bencina +

+Table of Contents

+ +
Overview of PortAudio +
Compiling for Macintosh OS 7,8,9 +
Compiling for Macintosh OS X +
Compiling for Windows (DirectSound and WMME) +
Compiling for ASIO on Windows or Mac OS +8,9 +
Compiling for Unix OSS +
Writing a Callback Function +
Initializing PortAudio +
Opening a Stream using Defaults +
Starting and Stopping a Stream +
Cleaning Up +
Utilities +
Querying for Devices +
Blocking Read/Write Functions +
Exploring the PortAudio Package
+home | contents | +previous |  next + + diff --git a/pd/portaudio/docs/portaudio_h.txt b/pd/portaudio/docs/portaudio_h.txt new file mode 100644 index 00000000..6d60086f --- /dev/null +++ b/pd/portaudio/docs/portaudio_h.txt @@ -0,0 +1,425 @@ +#ifndef PORT_AUDIO_H +#define PORT_AUDIO_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * PortAudio Portable Real-Time Audio Library + * PortAudio API Header File + * Latest version available at: http://www.audiomulch.com/portaudio/ + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +typedef int PaError; +typedef enum { + paNoError = 0, + + paHostError = -10000, + paInvalidChannelCount, + paInvalidSampleRate, + paInvalidDeviceId, + paInvalidFlag, + paSampleFormatNotSupported, + paBadIODeviceCombination, + paInsufficientMemory, + paBufferTooBig, + paBufferTooSmall, + paNullCallback, + paBadStreamPtr, + paTimedOut, + paInternalError +} PaErrorNum; + +/* + Pa_Initialize() is the library initialisation function - call this before + using the library. +*/ + +PaError Pa_Initialize( void ); + +/* + Pa_Terminate() is the library termination function - call this after + using the library. +*/ + +PaError Pa_Terminate( void ); + +/* + Return host specific error. + This can be called after receiving a paHostError. +*/ +long Pa_GetHostError( void ); + +/* + Translate the error number into a human readable message. +*/ +const char *Pa_GetErrorText( PaError errnum ); + +/* + Sample formats + + These are formats used to pass sound data between the callback and the + stream. Each device has a "native" format which may be used when optimum + efficiency or control over conversion is required. + + Formats marked "always available" are supported (emulated) by all devices. + + The floating point representation uses +1.0 and -1.0 as the respective + maximum and minimum. + +*/ + +typedef unsigned long PaSampleFormat; +#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/ +#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/ +#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/ +#define paInt24 ((PaSampleFormat) (1<<3)) +#define paPackedInt24 ((PaSampleFormat) (1<<4)) +#define paInt8 ((PaSampleFormat) (1<<5)) +#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */ +#define paCustomFormat ((PaSampleFormat) (1<<16)) + +/* + Device enumeration mechanism. + + Device ids range from 0 to Pa_CountDevices()-1. + + Devices may support input, output or both. Device 0 is always the "default" + device and should support at least stereo in and out if that is available + on the taget platform _even_ if this involves kludging an input/output + device on platforms that usually separate input from output. Other platform + specific devices are specified by positive device ids. +*/ + +typedef int PaDeviceID; +#define paNoDevice -1 + +typedef struct{ + int structVersion; + const char *name; + int maxInputChannels; + int maxOutputChannels; +/* Number of discrete rates, or -1 if range supported. */ + int numSampleRates; +/* Array of supported sample rates, or {min,max} if range supported. */ + const double *sampleRates; + PaSampleFormat nativeSampleFormats; +} PaDeviceInfo; + + +int Pa_CountDevices(); +/* + Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() + + Return the default device ID or paNoDevice if there is no devices. + The result can be passed to Pa_OpenStream(). + + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. + + set PA_RECOMMENDED_OUTPUT_DEVICE=1 + + The user should first determine the available device ID by using + the supplied application "pa_devs". +*/ +PaDeviceID Pa_GetDefaultInputDeviceID( void ); +PaDeviceID Pa_GetDefaultOutputDeviceID( void ); + +/* + PaTimestamp is used to represent a continuous sample clock with arbitrary + start time useful for syncronisation. The type is used in the outTime + argument to the callback function and the result of Pa_StreamTime() +*/ + +typedef double PaTimestamp; + +/* + Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure + referring to the device specified by id. + If id is out of range the function returns NULL. + + The returned structure is owned by the PortAudio implementation and must + not be manipulated or freed. The pointer is guaranteed to be valid until + between calls to Pa_Initialize() and Pa_Terminate(). +*/ + +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID devID ); + +/* + PortAudioCallback is implemented by clients of the portable audio api. + + inputBuffer and outputBuffer are arrays of interleaved samples, + the format, packing and number of channels used by the buffers are + determined by parameters to Pa_OpenStream() (see below). + + framesPerBuffer is the number of sample frames to be processed by the callback. + + outTime is the time in samples when the buffer(s) processed by + this callback will begin being played at the audio output. + See also Pa_StreamTime() + + userData is the value of a user supplied pointer passed to Pa_OpenStream() + intended for storing synthesis data etc. + + return value: + The callback can return a nonzero value to stop the stream. This may be + useful in applications such as soundfile players where a specific duration + of output is required. However, it is not necessary to utilise this mechanism + as StopStream() will also terminate the stream. A callback returning a + nonzero value must fill the entire outputBuffer. + + NOTE: None of the other stream functions may be called from within the + callback function except for Pa_GetCPULoad(). + +*/ + +typedef int (PortAudioCallback)( + void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); + + +/* + Stream flags + + These flags may be supplied (ored together) in the streamFlags argument to + the Pa_OpenStream() function. + + [ suggestions? ] +*/ + +#define paNoFlag (0) +#define paClipOff (1<<0) /* disable defult clipping of out of range samples */ +#define paDitherOff (1<<1) /* disable default dithering */ +#define paPlatformSpecificFlags (0x00010000) +typedef unsigned long PaStreamFlags; + +/* + A single PortAudioStream provides multiple channels of real-time + input and output audio streaming to a client application. + Pointers to PortAudioStream objects are passed between PortAudio functions. +*/ + +typedef void PortAudioStream; +#define PaStream PortAudioStream + +/* + Pa_OpenStream() opens a stream for either input, output or both. + + stream is the address of a PortAudioStream pointer which will receive + a pointer to the newly opened stream. + + inputDevice is the id of the device used for input (see PaDeviceID above.) + inputDevice may be paNoDevice to indicate that an input device is not required. + + numInputChannels is the number of channels of sound to be delivered to the + callback. It can range from 1 to the value of maxInputChannels in the + device input record for the device specified in the inputDevice parameter. + If inputDevice is paNoDevice numInputChannels is ignored. + + inputSampleFormat is the format of inputBuffer provided to the callback + function. inputSampleFormat may be any of the formats described by the + PaSampleFormat enumeration (see above). PortAudio guarantees support for + the sound devices native formats (nativeSampleFormats in the device info + record) and additionally 16 and 32 bit integer and 32 bit floating point + formats. Support for other formats is implementation defined. + + inputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or stream processing. + inputDriverInfo is never required for correct operation. If not used + inputDriverInfo should be NULL. + + outputDevice is the id of the device used for output (see PaDeviceID above.) + outputDevice may be paNoDevice to indicate that an output device is not required. + + numOutputChannels is the number of channels of sound to be supplied by the + callback. See the definition of numInputChannels above for more details. + + outputSampleFormat is the sample format of the outputBuffer filled by the + callback function. See the definition of inputSampleFormat above for more + details. + + outputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or stream processing. + outputDriverInfo is never required for correct operation. If not used + outputDriverInfo should be NULL. + + sampleRate is the desired sampleRate for input and output + + framesPerBuffer is the length in sample frames of all internal sample buffers + used for communication with platform specific audio routines. Wherever + possible this corresponds to the framesPerBuffer parameter passed to the + callback function. + + numberOfBuffers is the number of buffers used for multibuffered + communication with the platform specific audio routines. This parameter is + provided only as a guide - and does not imply that an implementation must + use multibuffered i/o when reliable double buffering is available (such as + SndPlayDoubleBuffer() on the Macintosh.) + + streamFlags may contain a combination of flags ORed together. + These flags modify the behavior of the + streaming process. Some flags may only be relevant to certain buffer formats. + + callback is a pointer to a client supplied function that is responsible + for processing and filling input and output buffers (see above for details.) + + userData is a client supplied pointer which is passed to the callback + function. It could for example, contain a pointer to instance data necessary + for processing the audio buffers. + + return value: + Apon success Pa_OpenStream() returns PaNoError and places a pointer to a + valid PortAudioStream in the stream argument. The stream is inactive (stopped). + If a call to Pa_OpenStream() fails a nonzero error code is returned (see + PAError above) and the value of stream is invalid. + +*/ + +PaError Pa_OpenStream( PortAudioStream** stream, + PaDeviceID inputDevice, + int numInputChannels, + PaSampleFormat inputSampleFormat, + void *inputDriverInfo, + PaDeviceID outputDevice, + int numOutputChannels, + PaSampleFormat outputSampleFormat, + void *outputDriverInfo, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PaStreamFlags streamFlags, + PortAudioCallback *callback, + void *userData ); + + +/* + Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that + opens the default input and/or ouput devices. Most parameters have + identical meaning to their Pa_OpenStream() counterparts, with the following + exceptions: + + If either numInputChannels or numOutputChannels is 0 the respective device + is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() ) + + sampleFormat applies to both the input and output buffers. +*/ + +PaError Pa_OpenDefaultStream( PortAudioStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PortAudioCallback *callback, + void *userData ); + +/* + Pa_CloseStream() closes an audio stream, flushing any pending buffers. +*/ + +PaError Pa_CloseStream( PortAudioStream* ); + +/* + Pa_StartStream() and Pa_StopStream() begin and terminate audio processing. + Pa_StopStream() waits until all pending audio buffers have been played. + Pa_AbortStream() stops playing immediately without waiting for pending + buffers to complete. +*/ + +PaError Pa_StartStream( PortAudioStream *stream ); + +PaError Pa_StopStream( PortAudioStream *stream ); + +PaError Pa_AbortStream( PortAudioStream *stream ); + +/* + Pa_StreamActive() returns one when the stream is playing audio, + zero when not playing, or a negative error number if the + stream is invalid. + The stream is active between calls to Pa_StartStream() and Pa_StopStream(), + but may also become inactive if the callback returns a non-zero value. + In the latter case, the stream is considered inactive after the last + buffer has finished playing. +*/ + +PaError Pa_StreamActive( PortAudioStream *stream ); + +/* + Pa_StreamTime() returns the current output time for the stream in samples. + This time may be used as a time reference (for example syncronising audio to + MIDI). +*/ + +PaTimestamp Pa_StreamTime( PortAudioStream *stream ); + +/* + The "CPU Load" is a fraction of total CPU time consumed by the + stream's audio processing. + A value of 0.5 would imply that PortAudio and the sound generating + callback was consuming roughly 50% of the available CPU time. + This function may be called from the callback function or the application. +*/ +double Pa_GetCPULoad( PortAudioStream* stream ); + +/* + Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for + the current host based on minimum latency. + On the PC, for the DirectSound implementation, latency can be optionally set + by user by setting an environment variable. + For example, to set latency to 200 msec, put: + + set PA_MIN_LATENCY_MSEC=200 + + in the AUTOEXEC.BAT file and reboot. + If the environment variable is not set, then the latency will be determined + based on the OS. Windows NT has higher latency than Win95. +*/ + +int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ); + +/* + Sleep for at least 'msec' milliseconds. + You may sleep longer than the requested time so don't rely + on this for accurate musical timing. +*/ +void Pa_Sleep( long msec ); + +/* + Return size in bytes of a single sample in a given PaSampleFormat + or paSampleFormatNotSupported. +*/ +PaError Pa_GetSampleSize( PaSampleFormat format ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORT_AUDIO_H */ diff --git a/pd/portaudio/docs/portaudio_icmc2001.pdf b/pd/portaudio/docs/portaudio_icmc2001.pdf new file mode 100644 index 00000000..dd074b7d Binary files /dev/null and b/pd/portaudio/docs/portaudio_icmc2001.pdf differ diff --git a/pd/portaudio/docs/proposals.html b/pd/portaudio/docs/proposals.html new file mode 100644 index 00000000..0f9b8cb6 --- /dev/null +++ b/pd/portaudio/docs/proposals.html @@ -0,0 +1,36 @@ + + +Proposed Changes to PortAudio API + + + + + +  +
+ + + +
+
+

Proposed Changes to PortAudio API

+
+
+

PortAudio Home Page

+ +

Updated: July 27, 2002

+

The Proposals Have Moved

+ +

+All PortAudio Enhancement Proposal documentation has moved. On the web site, it is now located at: +http://www.portaudio.com/docs/proposals + +On the CVS server it is now located in a module named "pa_proposals". +

+ + + + diff --git a/pd/portaudio/docs/releases.html b/pd/portaudio/docs/releases.html new file mode 100644 index 00000000..aec80a1c --- /dev/null +++ b/pd/portaudio/docs/releases.html @@ -0,0 +1,339 @@ + + + + + + + + + PortAudio Release Notes + + +  +
+ + + +
+
+

+PortAudio - Release Notes

+
+ +

Link to PortAudio Home Page +

+V18 - 5/6/02

+ +
All source code and documentation now under CVS. +

Ran most of the code through AStyle +to cleanup ragged indentation caused by using different editors. Used this +command: +
   astyle --style=ansi -c -o --convert-tabs --indent-preprocessor +*.c

+ +
Added "pa_common/pa_convert.c" for Mac OS X. Start of new conversion +utilities. +

ASIO +

    +
  • +New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables,
  • + +
  • +Cleanup of Pa_ASIO_Callback_Input
  • + +
  • +Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo
  • + +
  • +Deallocate all resources in PaHost_Term for cases where Pa_CloseStream +is not called properly
  • + +
  • +New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows. +Allows use by multiple threads.
  • + +
  • +Correct error code management in PaHost_Term, removed various compiler +warning
  • + +
  • +Add Mac includes for <Devices.h> and <Timer.h>
  • + +
  • +Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better +error handling
  • +
+Mac OS X +
    +
  • +Major cleanup and improvements.
  • + +
  • +Fixed device queries for numChannels and sampleRates,
  • + +
  • +Audio input works if using same CoreAudio device (some HW devices make +separate CoreAudio devices).
  • + +
  • +Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file.
  • + +
  • +Return error if opened in mono mode cuz not supported.
  • + +
  • +Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally.
  • + +
  • +Use getrusage() instead of gettimeofday() for CPU Load calculation.
  • +
+Windows MME +
    +
  • +Fixed bug that caused TIMEOUT in Pa_StopStream(). Added check for past_StopSoon() +in Pa_TimeSlice(). Thanks Julien Maillard.
  • + +
  • +Detect Win XP versus NT, use lower latency.
  • + +
  • +Fix DBUG typo;
  • + +
  • +removed init of CurrentCount which was not compiling on Borland
  • + +
  • +general cleanup, factored streamData alloc and cpu usage initialization
  • + +
  • +stopped counting WAVE_MAPPER when there were no audio cards plugged in
  • +
+Windows DirectSound +
    +
  • +Detect Win XP and Win 2K properly when determining latency.
  • +
+Unix OSS +
    +
  • +Use high real-time priority if app is running with root priveledges. Lowers +latency.
  • + +
  • +Added watch dog thread that prevents real-time thread from hogging CPU +and hanging the computer.
  • + +
  • +Check error return from read() and write().
  • + +
  • +Check CPU endianness instead of assuming Little Endian.
  • +
+
+ +

+V17 - 10/15/01

+ +
Unix OSS +
    +
  • +Set num channels back to two after device query for ALSA. This fixed a +bug in V16 that sometimes caused a failure when querying for the sample +rates. Thanks Stweart Greenhill.
  • +
+
+ +
+

+Macintosh Sound Manager

+ +
    +
  • +Use NewSndCallBackUPP() for CARBON compatibility.
  • +
+
+ +

+V16 - 9/27/01

+ +
Added Alpha implementations for ASIO, SGI, and BeOS! +
  +
  • +CPULoad is now calculated based on the time spent to generate a known number +of frames. This is more accurate than a simple percentage of real-time. +Implemented in pa_unix_oss, pa_win_wmme and pa_win_ds.
  • + +
  • +Fix dither and shift for recording PaUInt8 format data.
  • + +
  • +Added "patest_maxsines.c" which tests Pa_GetCPULoad().
  • +
    + +
    +

    +Windows WMME

    + +
      +
    • +sDevicePtrs now allocated using GlobalAlloc(). This prevents a +crash in Pa_Terminate() on Win2000. Thanks Mike Berry for finding this. +Thanks Mike Berry.
    • + +
    • +Pass process instead of thread to SetPriorityClass(). This fixes +a bug that caused the priority to not be increased. Thanks to Alberto di +Bene for spotting this.
    • +
    + +

    +Windows DirectSound

    + +
      +
    • +Casts for compiling with __MWERKS__ CodeWarrior.
    • +
    + +

    +UNIX OSS

    + +
      +
    • +Derived from Linux OSS implementation.
    • + +
    • +Numerous patches from Heiko Purnhagen, Stephen Brandon, etc.
    • + +
    • +Improved query mechanism which often bailed out unnecessarily.
    • + +
    • +Removed sNumDevices and potential related bugs,
    • + +
    • +Use getenv("PA_MIN_LATENCY_MSEC") in code to set desired latency. +User can set by entering:
    • + +
          export PA_MIN_LATENCY_MSEC=40
    + +

    +Macintosh Sound Manager

    + +
      +
    • +Pass unused event to WaitNextEvent instead of NULL to prevent Mac OSX crash. +Thanks Dominic Mazzoni.
    • + +
    • +Use requested number of input channels.
    • + +
       
    +
    + +

    +V15 - 5/29/01

    + +
    +
      +
    • +New Linux OSS Beta
    • +
    + +

    +Windows WMME

    + +
      +
    • + sDevicePtrs now allocated based on sizeof(pointer). Was allocating +too much space.
    • + +
    • + Check for excessive numbers of channels. Some drivers reported bogus +numbers.
    • + +
    • +Apply Mike Berry's changes for CodeWarrior on PC including condition including +of memory.h, and explicit typecasting on memory allocation.
    • +
    + +

    +Macintosh Sound Manager

    + +
      +
    • +ScanInputDevices was setting sDefaultOutputDeviceID instead of sDefaultInputDeviceID.
    • + +
    • +Device Scan was crashing for anything other than siBadSoundInDevice, but +some Macs may return other errors! Caused failure to init on some G4s under +OS9.
    • + +
    • +Fix TIMEOUT in record mode.
    • + +
    • +Change CARBON_COMPATIBLE to TARGET_API_MAC_CARBON
    • +
    +
    + +

    +V14 - 2/6/01

    + +
    +
      +
    • +Added implementation for Windows MultiMedia Extensions (WMME) by Ross and +Phil
    • + +
    • +Changed Pa_StopStream() so that it waits for the buffers to drain.
    • + +
    • +Added Pa_AbortStream() that stops immediately without waiting.
    • + +
    • +Added new test: patest_stop.c to test above two mods.
    • + +
    • +Fixed Pa_StreamTime() so that it returns current play position instead +of the write position. Added "patest_sync.c" to demo audio/video sync.
    • + +
    • +Improved stability of Macintosh implementation. Added timeouts to prevent +hangs.
    • + +
    • +Added Pa_GetSampleSize( PaSampleFormat format );
    • + +
    • +Changes some "int"s to "long"s so that PA works properly on Macintosh which +often compiles using 16 bit ints.
    • + +
    • +Added Implementation Guide
    • +
    +
    + +

    +V12 - 1/9/01

    + +
    +
      +
    • +Mac now scans for and queries all devices. But it does not yet support +selecting any other than the default device.
    • + +
    • +Blocking I/O calls renamed to separate them from the PortAudio API.
    • + +
    • +Cleaned up indentation problems with tabs versus spaces.
    • + +
    • +Now attempts to correct bogus sample rate info returned from DirectSound +device queries.
    • +
    +
    + + + 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/index.html b/pd/portaudio/index.html new file mode 100644 index 00000000..91e5b4c3 --- /dev/null +++ b/pd/portaudio/index.html @@ -0,0 +1,89 @@ + + + + + + + + + PortAudio Implementations for DirectSound + + +  +
    + + + +
    +
    +

    +PortAudio - Portable Audio Library

    +
    + +

    Last updated 5/6/02. +

    PortAudio is a cross platform, open-source, audio +I/O library proposed by Ross Bencina to the music-dsp +mailing list. It lets you write simple audio programs in 'C' that will +compile and run on Windows, Macintosh, Unix, BeOS. PortAudio is +intended to promote the exchange of audio synthesis software between developers +on different platforms. +

    For complete information on PortAudio and to download the latest releases, +please visit "http://www.portaudio.com". +
      +
      +

    +

    +Click here for Documentation

    + +

    +

    + +

    +Contacts and E-Mail List

    + +
      +
    • +If you are using or implementing PortAudio then please join the PortAudio +mail list generously administered by +Bill +Eldridge.
    • + +
    • +If you find bugs in one of these implementations, or have suggestions, +please e-mail them to Phil Burk.
    • + +
    • +If you make improvements to the library, please send them to us so we can +incorporate the improvements.
    • +
    + +

    +License

    +PortAudio Portable Real-Time Audio Library +
    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 ON INFRINGEMENT. +
    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. +
      + + 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/Callback_adaptation_.pdf b/pd/portaudio/pa_asio/Callback_adaptation_.pdf new file mode 100644 index 00000000..76bf6786 Binary files /dev/null and b/pd/portaudio/pa_asio/Callback_adaptation_.pdf 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 Binary files /dev/null and b/pd/portaudio/pa_asio/Pa_ASIO.pdf differ diff --git a/pd/portaudio/pa_asio/borland_asio_readme.txt b/pd/portaudio/pa_asio/borland_asio_readme.txt new file mode 100644 index 00000000..56e472b8 --- /dev/null +++ b/pd/portaudio/pa_asio/borland_asio_readme.txt @@ -0,0 +1,6 @@ +Steinberg's ASIO 2 SDK will compile but crash on +initialization if compiled with a Borland compiler. + +The problem is described, and a solution provided on +the following page: +http://www.audiomulch.com/~rossb/code/calliasio \ No newline at end of file diff --git a/pd/portaudio/pa_asio/pa_asio.cpp b/pd/portaudio/pa_asio/pa_asio.cpp index baed8ed4..c8ba58f4 100644 --- a/pd/portaudio/pa_asio/pa_asio.cpp +++ b/pd/portaudio/pa_asio/pa_asio.cpp @@ -1,10 +1,10 @@ /* - * $Id: pa_asio.cpp,v 1.7 2002/04/30 12:33:04 stephane Exp $ + * $Id: pa_asio.cpp,v 1.7.2.30 2002/12/03 06:30:39 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-2001 Stephane Letz, Phil Burk + * 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 @@ -57,2942 +57,2051 @@ 12-04-02 Add Mac includes for and : 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 - - TO DO : - - - Check Pa_StopSteam and Pa_AbortStream - - Optimization for Input only or Ouput only (really necessary ??) + 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 -#include -#include -#include + @todo implement underflow/overflow streamCallback statusFlags, paNeverDropInput. -#include "portaudio.h" -#include "pa_host.h" -#include "pa_trace.h" + @todo implement host api specific extension to set i/o buffer sizes in frames -#include "asiosys.h" -#include "asio.h" -#include "asiodrivers.h" + @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.) + @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable -#if MAC -#include -#include -#include -#else -#include -#include -#include -#endif + @todo implement IsFormatSupported -enum { - // number of input and outputs supported by the host application - // you can change these to higher or lower values - kMaxInputChannels = 32, - kMaxOutputChannels = 32 -}; + @todo work out how to implement stream stoppage from callback and + implement IsStreamActive properly -/* ASIO specific device information. */ -typedef struct internalPortAudioDevice -{ - PaDeviceInfo pad_Info; -} internalPortAudioDevice; + @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. -/* ASIO driver internal data storage */ -typedef struct PaHostSoundControl -{ - // ASIOInit() - ASIODriverInfo pahsc_driverInfo; + @todo investigate whether the asio processNow flag needs to be honoured - // ASIOGetChannels() - int32 pahsc_NumInputChannels; - int32 pahsc_NumOutputChannels; + @todo handle asioMessages() callbacks in a useful way, or at least document + what cases we don't handle. - // ASIOGetBufferSize() - sizes in frames per buffer - int32 pahsc_minSize; - int32 pahsc_maxSize; - int32 pahsc_preferredSize; - int32 pahsc_granularity; + @todo miscellaneous other FIXMEs - // ASIOGetSampleRate() - ASIOSampleRate pahsc_sampleRate; + @todo implement the following somewhere: - // ASIOOutputReady() - bool pahsc_postOutput; + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); +*/ - // ASIOGetLatencies () - int32 pahsc_inputLatency; - int32 pahsc_outputLatency; - // ASIOCreateBuffers () - ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's - // ASIOGetChannelInfo() - ASIOChannelInfo pahsc_channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's - // The above two arrays share the same indexing, as the data in them are linked together +#include +#include +#include +//#include - // Information from ASIOGetSamplePosition() - // data is converted to double floats for easier use, however 64 bit integer can be used, too - double nanoSeconds; - double samples; - double tcSamples; // time code samples +#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" - // bufferSwitchTimeInfo() - ASIOTime tInfo; // time info state - unsigned long sysRefTime; // system reference time, when bufferSwitch() was called +#include "asiosys.h" +#include "asio.h" +#include "asiodrivers.h" - // Signal the end of processing in this example - bool stopped; - - ASIOCallbacks pahsc_asioCallbacks; - - - int32 pahsc_userInputBufferFrameOffset; // Position in Input user buffer - int32 pahsc_userOutputBufferFrameOffset; // Position in Output user buffer - int32 pahsc_hostOutputBufferFrameOffset; // Position in Output ASIO buffer - - int32 past_FramesPerHostBuffer; // Number of frames in ASIO buffer - - int32 pahsc_InputBufferOffset; // Number of null frames for input buffer alignement - int32 pahsc_OutputBufferOffset; // Number of null frames for ouput buffer alignement - +/* #if MAC - UInt64 pahsc_EntryCount; - UInt64 pahsc_LastExitCount; -#elif WINDOWS - LARGE_INTEGER pahsc_EntryCount; - LARGE_INTEGER pahsc_LastExitCount; -#endif - - PaTimestamp pahsc_NumFramesDone; - - internalPortAudioStream *past; - -} PaHostSoundControl; - +#include +#include +#include +#else +*/ +/* +#include +#include +#include +*/ +/* +#endif +*/ -//---------------------------------------------------------- -#define PRINT(x) { printf x; fflush(stdout); } -#define ERR_RPT(x) PRINT(x) +/* external references */ +extern AsioDrivers* asioDrivers ; +bool loadAsioDriver(char *name); -#define DBUG(x) /* PRINT(x) */ -#define DBUGX(x) /* PRINT(x) /**/ /* 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) -#define PA_MAX_DEVICE_INFO (32) - -#define MIN_INT8 (-0x80) -#define MAX_INT8 (0x7F) - -#define MIN_INT8_FP ((float)-0x80) -#define MAX_INT8_FP ((float)0x7F) -#define MIN_INT16_FP ((float)-0x8000) -#define MAX_INT16_FP ((float)0x7FFF) -#define MIN_INT16 (-0x8000) -#define MAX_INT16 (0x7FFF) -#define MAX_INT32_FP (2147483520.0f) /* 0x0x7FFFFF80 - seems safe */ -/************************************************************************************/ -/****************** Data ************************************************************/ -/************************************************************************************/ -static int sNumDevices = 0; -static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 }; -static int32 sPaHostError = 0; -static int sDefaultOutputDeviceID = 0; -static int sDefaultInputDeviceID = 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, void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); + +/* our ASIO callback functions */ -PaHostSoundControl asioDriverInfo = {0}; - -#ifdef MAC -static bool swap = true; -#elif WINDOWS -static bool swap = false; -#endif - -// Prototypes 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 void Pa_StartUsageCalculation( internalPortAudioStream *past ); -static void Pa_EndUsageCalculation( internalPortAudioStream *past ); - -static void Pa_ASIO_Convert_Inter_Input( - ASIOBufferInfo* nativeBuffer, - void* inputBuffer, - long NumInputChannels, - long NumOuputChannels, - long framePerBuffer, - long hostFrameOffset, - long userFrameOffset, - ASIOSampleType nativeFormat, - PaSampleFormat paFormat, - PaStreamFlags flags, - long index); - -static void Pa_ASIO_Convert_Inter_Output( - ASIOBufferInfo* nativeBuffer, - void* outputBuffer, - long NumInputChannels, - long NumOuputChannels, - long framePerBuffer, - long hostFrameOffset, - long userFrameOffset, - ASIOSampleType nativeFormat, - PaSampleFormat paFormat, - PaStreamFlags flags, - long index); - -static void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer, - ASIOSampleType nativeFormat, - long NumInputChannels, - long NumOuputChannels, - long index, - long hostFrameOffset, - long frames); - -static void Pa_ASIO_Callback_Input(long index); -static void Pa_ASIO_Callback_Output(long index, long framePerBuffer); -static void Pa_ASIO_Callback_End(); -static void Pa_ASIO_Clear_User_Buffers(); - -// Some external references -extern AsioDrivers* asioDrivers ; -bool loadAsioDriver(char *name); -unsigned long get_sys_reference_time(); - - -/************************************************************************************/ -/****************** Macro ************************************************************/ -/************************************************************************************/ -#define SwapLong(v) ((((v)>>24)&0xFF)|(((v)>>8)&0xFF00)|(((v)&0xFF00)<<8)|(((v)&0xFF)<<24)) ; -#define SwapShort(v) ((((v)>>8)&0xFF)|(((v)&0xFF)<<8)) ; +static ASIOCallbacks asioCallbacks_ = + { bufferSwitch, sampleRateChanged, asioMessages, bufferSwitchTimeInfo }; -#define ClipShort(v) (((v)MAX_INT16)?MAX_INT16:(v))) -#define ClipChar(v) (((v)MAX_INT8)?MAX_INT8:(v))) -#define ClipFloat(v) (((v)<-1.0f)?-1.0f:(((v)>1.0f)?1.0f:(v))) - -#ifndef min -#define min(a,b) ((a)<(b)?(a):(b)) -#endif - -#ifndef max -#define max(a,b) ((a)>=(b)?(a):(b)) -#endif +#define PA_ASIO_SET_LAST_HOST_ERROR( errorCode, errorText ) \ + PaUtil_SetLastHostErrorInfo( paASIO, errorCode, errorText ) -static bool Pa_ASIO_loadAsioDriver(char *name) +static const char* PaAsio_GetAsioErrorText( ASIOError asioError ) { - #ifdef WINDOWS - CoInitialize(0); - #endif - return loadAsioDriver(name); + 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; } - -// Utilities for alignement buffer size computation -static int PGCD (int a, int b) {return (b == 0) ? a : PGCD (b,a%b);} -static int PPCM (int a, int b) {return (a*b) / PGCD (a,b);} +#define PA_ASIO_SET_LAST_ASIO_ERROR( asioError ) \ + PaUtil_SetLastHostErrorInfo( paASIO, asioError, PaAsio_GetAsioErrorText( asioError ) ) + -// Takes the size of host buffer and user buffer : returns the number of frames needed for buffer alignement -static int Pa_ASIO_CalcFrameShift (int M, int N) -{ - int res = 0; - for (int i = M; i < PPCM (M,N) ; i+=M) { res = max (res, i%N); } - return res; -} +/* PaAsioHostApiRepresentation - host api datastructure specific to this implementation */ -// We have the following relation : -// Pa_ASIO_CalcFrameShift (M,N) + M = Pa_ASIO_CalcFrameShift (N,M) + N - -/* ASIO sample type to PortAudio sample type conversion */ -static PaSampleFormat Pa_ASIO_Convert_SampleFormat(ASIOSampleType type) +typedef struct { - switch (type) { - - case ASIOSTInt16MSB: - case ASIOSTInt16LSB: - case ASIOSTInt32MSB16: - case ASIOSTInt32LSB16: - return paInt16; - - case ASIOSTFloat32MSB: - case ASIOSTFloat32LSB: - case ASIOSTFloat64MSB: - case ASIOSTFloat64LSB: - return paFloat32; - - case ASIOSTInt32MSB: - case ASIOSTInt32LSB: - case ASIOSTInt32MSB18: - case ASIOSTInt32MSB20: - case ASIOSTInt32MSB24: - case ASIOSTInt32LSB18: - case ASIOSTInt32LSB20: - case ASIOSTInt32LSB24: - return paInt32; - - case ASIOSTInt24MSB: - case ASIOSTInt24LSB: - return paInt24; - - default: - return paCustomFormat; - } -} + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; -/* Allocate ASIO buffers, initialise channels */ -static ASIOError Pa_ASIO_CreateBuffers (PaHostSoundControl *asioDriverInfo, long InputChannels, - long OutputChannels, long framesPerBuffer) -{ - ASIOError err; - int i; - - ASIOBufferInfo *info = asioDriverInfo->bufferInfos; - - // Check parameters - if ((InputChannels > kMaxInputChannels) || (OutputChannels > kMaxInputChannels)) return ASE_InvalidParameter; - - for(i = 0; i < InputChannels; i++, info++){ - info->isInput = ASIOTrue; - info->channelNum = i; - info->buffers[0] = info->buffers[1] = 0; - } - - for(i = 0; i < OutputChannels; i++, info++){ - info->isInput = ASIOFalse; - info->channelNum = i; - info->buffers[0] = info->buffers[1] = 0; - } - - // Set up the asioCallback structure and create the ASIO data buffer - asioDriverInfo->pahsc_asioCallbacks.bufferSwitch = &bufferSwitch; - asioDriverInfo->pahsc_asioCallbacks.sampleRateDidChange = &sampleRateChanged; - asioDriverInfo->pahsc_asioCallbacks.asioMessage = &asioMessages; - asioDriverInfo->pahsc_asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo; - - DBUG(("PortAudio : ASIOCreateBuffers with size = %ld \n", framesPerBuffer)); - - err = ASIOCreateBuffers( asioDriverInfo->bufferInfos, InputChannels+OutputChannels, - framesPerBuffer, &asioDriverInfo->pahsc_asioCallbacks); - if (err != ASE_OK) return err; - - // Initialise buffers - for (i = 0; i < InputChannels + OutputChannels; i++) - { - asioDriverInfo->pahsc_channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum; - asioDriverInfo->pahsc_channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput; - err = ASIOGetChannelInfo(&asioDriverInfo->pahsc_channelInfos[i]); - if (err != ASE_OK) break; - } + PaUtilAllocationGroup *allocations; - err = ASIOGetLatencies(&asioDriverInfo->pahsc_inputLatency, &asioDriverInfo->pahsc_outputLatency); - - DBUG(("PortAudio : InputLatency = %ld latency = %ld msec \n", - asioDriverInfo->pahsc_inputLatency, - (long)((asioDriverInfo->pahsc_inputLatency*1000)/ asioDriverInfo->past->past_SampleRate))); - DBUG(("PortAudio : OuputLatency = %ld latency = %ld msec \n", - asioDriverInfo->pahsc_outputLatency, - (long)((asioDriverInfo->pahsc_outputLatency*1000)/ asioDriverInfo->past->past_SampleRate))); - - return err; + /* 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. + */ + int driverOpen; } +PaAsioHostApiRepresentation; /* - Query ASIO driver info : - - First we get all available ASIO drivers located in the ASIO folder, - then try to load each one. For each loaded driver, get all needed informations. + Retrieve driver names from ASIO, returned in a char** + allocated in . */ -static PaError Pa_ASIO_QueryDeviceInfo( internalPortAudioDevice * ipad ) +static char **GetAsioDriverNames( PaUtilAllocationGroup *group, long driverCount ) { + char **result = 0; + int i; + + result =(char**)PaUtil_GroupAllocateMemory( + group, sizeof(char*) * driverCount ); + if( !result ) + goto error; -#define NUM_STANDARDSAMPLINGRATES 3 /* 11.025, 22.05, 44.1 */ -#define NUM_CUSTOMSAMPLINGRATES 9 /* must be the same number of elements as in the array below */ -#define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES) + result[0] = (char*)PaUtil_GroupAllocateMemory( + group, 32 * driverCount ); + if( !result[0] ) + goto error; - ASIOSampleRate possibleSampleRates[] - = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0}; - - ASIOChannelInfo channelInfos; - long InputChannels,OutputChannels; - double *sampleRates; - char* names[PA_MAX_DEVICE_INFO] ; - PaDeviceInfo *dev; - int i; - int numDrivers; - ASIOError asioError; - - /* Allocate names */ - for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) { - names[i] = (char*)PaHost_AllocateFastMemory(32); - /* check memory */ - if(!names[i]) return paInsufficientMemory; - } - - /* MUST BE CHECKED : to force fragments loading on Mac */ - Pa_ASIO_loadAsioDriver("dummy"); - - /* Get names of all available ASIO drivers */ - asioDrivers->getDriverNames(names,PA_MAX_DEVICE_INFO); - - /* Check all available ASIO drivers */ -#if MAC - numDrivers = asioDrivers->getNumFragments(); -#elif WINDOWS - numDrivers = asioDrivers->asioGetNumDev(); -#endif - DBUG(("PaASIO_QueryDeviceInfo: numDrivers = %d\n", numDrivers )); + for( i=0; igetDriverNames( result, driverCount ); - #if WINDOWS - asioDriverInfo.pahsc_driverInfo.asioVersion = 2; // FIXME - is this right? PLB - asioDriverInfo.pahsc_driverInfo.sysRef = GetDesktopWindow(); // FIXME - is this right? PLB - #endif - - /* If the driver can be loaded : */ - if ( !Pa_ASIO_loadAsioDriver(names[driver]) ) - { - DBUG(("PaASIO_QueryDeviceInfo could not loadAsioDriver %s\n", names[driver])); - } - else if( (asioError = ASIOInit(&asioDriverInfo.pahsc_driverInfo)) != ASE_OK ) - { - DBUG(("PaASIO_QueryDeviceInfo: ASIOInit returned %d for %s\n", asioError, names[driver])); - } - else if( (ASIOGetChannels(&InputChannels, &OutputChannels) != ASE_OK)) - { - DBUG(("PaASIO_QueryDeviceInfo could not ASIOGetChannels for %s\n", names[driver])); - } - else - { - /* Gets the name */ - dev = &(ipad[sNumDevices].pad_Info); - dev->name = names[driver]; - names[driver] = 0; - - /* Gets Input and Output channels number */ - dev->maxInputChannels = InputChannels; - dev->maxOutputChannels = OutputChannels; - - DBUG(("PaASIO_QueryDeviceInfo: InputChannels = %d\n", InputChannels )); - DBUG(("PaASIO_QueryDeviceInfo: OutputChannels = %d\n", OutputChannels )); - - /* Make room in case device supports all rates. */ - sampleRates = (double*)PaHost_AllocateFastMemory(MAX_NUMSAMPLINGRATES * sizeof(double)); - /* check memory */ - if (!sampleRates) { - ASIOExit(); - return paInsufficientMemory; - } - dev->sampleRates = sampleRates; - dev->numSampleRates = 0; - - /* Loop through the possible sampling rates and check each to see if the device supports it. */ - for (int index = 0; index < MAX_NUMSAMPLINGRATES; index++) { - if (ASIOCanSampleRate(possibleSampleRates[index]) != ASE_NoClock) { - DBUG(("PortAudio : possible sample rate = %d\n", (long)possibleSampleRates[index])); - dev->numSampleRates += 1; - *sampleRates = possibleSampleRates[index]; - sampleRates++; - } - } - - /* We assume that all channels have the same SampleType, so check the first */ - channelInfos.channel = 0; - channelInfos.isInput = 1; - ASIOGetChannelInfo(&channelInfos); - - dev->nativeSampleFormats = Pa_ASIO_Convert_SampleFormat(channelInfos.type); - - /* unload the driver */ - ASIOExit(); - sNumDevices++; - } - } - - /* free only unused names */ - for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) if (names[i]) PaHost_FreeFastMemory(names[i],32); - - return paNoError; +error: + return result; } -//---------------------------------------------------------------------------------- -// TAKEN FROM THE ASIO SDK: -static void sampleRateChanged(ASIOSampleRate sRate) -{ - // 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. -} -//---------------------------------------------------------------------------------- -// TAKEN FROM THE ASIO SDK: -long asioMessages(long selector, long value, void* message, double* opt) +static PaSampleFormat AsioSampleTypeToPaNativeSampleFormat(ASIOSampleType type) { - // currently the parameters "value", "message" and "opt" are not used. - long ret = 0; - 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. - 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; -} - - -//---------------------------------------------------------------------------------- -// 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 + switch (type) { + case ASIOSTInt16MSB: + case ASIOSTInt16LSB: + return paInt16; + case ASIOSTFloat32MSB: + case ASIOSTFloat32LSB: + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + return paFloat32; -static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow) -{ - // 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. - - // static processedSamples = 0; - int result = 0; - - // store the timeInfo for later use - asioDriverInfo.tInfo = *timeInfo; + case ASIOSTInt32MSB: + case ASIOSTInt32LSB: + case ASIOSTInt32MSB16: + case ASIOSTInt32LSB16: + case ASIOSTInt32MSB18: + case ASIOSTInt32MSB20: + case ASIOSTInt32MSB24: + case ASIOSTInt32LSB18: + case ASIOSTInt32LSB20: + case ASIOSTInt32LSB24: + return paInt32; - // 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; + case ASIOSTInt24MSB: + case ASIOSTInt24LSB: + return paInt24; - if (timeInfo->timeInfo.flags & kSamplePositionValid) - asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition); - else - asioDriverInfo.samples = 0; + default: + return paCustomFormat; + } +} - 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(); +static int BytesPerAsioSample( ASIOSampleType sampleType ) +{ + switch (sampleType) { + case ASIOSTInt16MSB: + case ASIOSTInt16LSB: + return 2; -#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 + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + return 8; - // To avoid the callback accessing a desallocated stream - if( asioDriverInfo.past == NULL) return 0L; + 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; - // Keep sample position - asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo; + case ASIOSTInt24MSB: + case ASIOSTInt24LSB: + return 3; - /* Has a user callback returned '1' to indicate finished at the last ASIO callback? */ - if( asioDriverInfo.past->past_StopSoon ) { - - Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, - asioDriverInfo.pahsc_channelInfos[0].type, - asioDriverInfo.pahsc_NumInputChannels , - asioDriverInfo.pahsc_NumOutputChannels, - index, - 0, - asioDriverInfo.past_FramesPerHostBuffer); - - asioDriverInfo.past->past_IsActive = 0; - - // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place - if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady(); - - }else { - - /* CPU usage */ - Pa_StartUsageCalculation(asioDriverInfo.past); - - Pa_ASIO_Callback_Input(index); - - // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place - if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady(); - - Pa_ASIO_Callback_End(); - - /* CPU usage */ - Pa_EndUsageCalculation(asioDriverInfo.past); - } - - return 0L; + default: + return 0; + } } -//---------------------------------------------------------------------------------- -void bufferSwitch(long index, ASIOBool processNow) -{ - // 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)); +static void Swap16( void *buffer, long shift, long count ) +{ + unsigned short *p = (unsigned short*)buffer; + unsigned short temp; + (void) shift; /* unused parameter */ - // 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, processNow); + while( count-- ) + { + temp = *p; + *p++ = (unsigned short)((temp<<8) | (temp>>8)); + } } -//---------------------------------------------------------------------------------- -unsigned long get_sys_reference_time() -{ - // get the system reference time - #if WINDOWS - return timeGetTime(); - #elif MAC - static const double twoRaisedTo32 = 4294967296.; - UnsignedWide ys; - Microseconds(&ys); - double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo); - return (unsigned long)(r / 1000.); - #endif +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)); -/************************************************************* -** Calculate 2 LSB dither signal with a triangular distribution. -** Ranged properly for adding to a 32 bit integer prior to >>15. -*/ -#define DITHER_BITS (15) -#define DITHER_SCALE (1.0f / ((1<>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS)); - /* High pass filter to reduce audibility. */ - highPass = current - previous; - previous = current; - return highPass; -} + unsigned long *p = (unsigned long*)buffer; + unsigned long temp; + (void) shift; /* unused parameter */ -// TO BE COMPLETED WITH ALL SUPPORTED PA SAMPLE TYPES + while( count-- ) + { + temp = *p; + *p++ = PA_SWAP32_( temp); + } +} -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Input_Int16_Float32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap) +static void SwapShiftLeft32( void *buffer, long shift, long count ) { - long temp; - int i,j; - - for( j=0; j> shift; + *p++ = PA_SWAP32_( temp); + } } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// MUST BE TESTED -static void Input_Float32_Float32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap) +static void ShiftLeft32( void *buffer, long shift, long count ) { - unsigned long temp; - int i,j; - - for( j=0; j> shift; + } } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Input_Int32_Int32 (ASIOBufferInfo* nativeBuffer, long *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,bool swap) +#define PA_SWAP_( x, y ) temp=x; x = y; y = temp; + +static void Swap64ConvertFloat64ToFloat32( void *buffer, long shift, long count ) { - long temp; - int i,j; + 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] ); - for( j=0; j>16); - userBufPtr += NumInputChannels; - } - } - } - else - { - for( j=0; j> 1) + Pa_TriangularDither(); - temp = temp >> 15; - temp = (short) ClipShort(temp); - *userBufPtr = (short)temp; - userBufPtr += NumInputChannels; - } - } + 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--; + } } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// MUST BE TESTED -static void Input_Float32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap) +static void ConvertFloat32ToFloat64( void *buffer, long shift, long count ) { - unsigned long temp; - int i,j; - - if( flags & paDitherOff ) - { - for( j=0; j>8); - userBufPtr += NumInputChannels; - } - } - } - else - { - for( j=0; j> 8; - temp = ClipShort(temp); - *userBufPtr = (char)(temp>>8); - userBufPtr += NumInputChannels; - } - } - } + *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 Input_Int32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset, uint32 flags,bool swap) + +static void SelectPaToAsioConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift ) { - long temp; - int i,j; - - if( flags & paDitherOff ) - { - for( j=0; j>24); - userBufPtr += NumInputChannels; - } - } - } - else - { - for( j=0; j>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED) - temp += Pa_TriangularDither() >> 8; - temp = ClipShort(temp); - *userBufPtr = (char)(temp >> 8); - userBufPtr += NumInputChannels; - } - } - } + *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; + } } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// MUST BE TESTED -static void Input_Float32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap) +typedef struct PaAsioDeviceInfo { - unsigned long temp; - int i,j; - - if( flags & paDitherOff ) - { - for( j=0; j>8) + 0x80); - userBufPtr += NumInputChannels; - } - } - } - else - { - for( j=0; j> 8; - temp = ClipShort(temp); - *userBufPtr = (unsigned char)((temp>>8) + 0x80); - userBufPtr += NumInputChannels; - } - } - } -} + if( result == paNoError ) + { + result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Input_Int32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap) -{ - long temp; - int i,j; - - if( flags & paDitherOff ) + if( result == paNoError ) { - for( j=0; j>24) + 0x80); - userBufPtr += NumInputChannels; - } - } - } - else - { - for( j=0; j>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED) - temp += Pa_TriangularDither() >> 8; - temp = ClipShort(temp); - *userBufPtr = (unsigned char)((temp>>8) + 0x80); - userBufPtr += NumInputChannels; - } - } + PaAsioDeviceInfo *asioDeviceInfo = + (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; + + *minLatency = asioDeviceInfo->minBufferSize; + *maxLatency = asioDeviceInfo->maxBufferSize; + *preferredLatency = asioDeviceInfo->preferredBufferSize; + *granularity = asioDeviceInfo->bufferGranularity; } + } + + return result; } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// MUST BE TESTED -static void Input_Float32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap) -{ - unsigned long temp; - int i,j; - - if( flags & paDitherOff ) - { - for( j=0; j 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 *info ) { - long temp; - int i,j; - - if( flags & paClipOff ) - { - for (j= 0; j < NumOuputChannels; j++) - { - long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; i(driverName) ) ) + { + result = paUnanticipatedHostError; + PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" ); + goto error; + } + + if( (asioError = ASIOInit( &info->asioDriverInfo )) != ASE_OK ) + { + result = paUnanticipatedHostError; + PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); + goto error; + } + else + { + asioIsInitialized = 1; + } + + if( (asioError = ASIOGetChannels(&info->numInputChannels, + &info->numOutputChannels)) != ASE_OK ) + { + result = paUnanticipatedHostError; + PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); + goto error; + } + + if( (asioError = ASIOGetBufferSize(&info->bufferMinSize, + &info->bufferMaxSize, &info->bufferPreferredSize, + &info->bufferGranularity)) != ASE_OK ) + { + result = paUnanticipatedHostError; + PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); + goto error; + } + + if( ASIOOutputReady() == ASE_OK ) + info->postOutput = true; + else + info->postOutput = false; + + return result; + +error: + if( asioIsInitialized ) + ASIOExit(); + return result; } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// MUST BE TESTED +#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 }; - static void Output_Float32_Float32 (ASIOBufferInfo* nativeBuffer, float *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap) + +PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) { - long temp; - int i,j; - - if( flags & paClipOff ) - { - for (j= 0; j < NumOuputChannels; j++) - { - float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; iallocations = PaUtil_CreateAllocationGroup(); + if( !asioHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + asioHostApi->driverOpen = 0; + + *hostApi = &asioHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + + (*hostApi)->info.type = paASIO; + (*hostApi)->info.name = "ASIO"; + (*hostApi)->info.deviceCount = 0; + + #ifdef WINDOWS + CoInitialize(0); + #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 ) { - for (j= 0; j < NumOuputChannels; j++) - { - float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - float *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; i> 16); - if (swap) temp = SwapShort(temp); - asioBufPtr[i] = (short)temp; - userBufPtr += NumOuputChannels; - } - } - } - else + /* 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 ) { - for (j= 0; j < NumOuputChannels; j++) - { - short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; i> 1) + Pa_TriangularDither(); - temp = temp >> 15; - temp = (short) ClipShort(temp); - if (swap) temp = SwapShort(temp); - asioBufPtr[i] = (short)temp; - userBufPtr += NumOuputChannels; - } - } + result = paInsufficientMemory; + goto error; } -} -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Output_Int32_Int32(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap) -{ - long temp; - int i,j; - - for (j= 0; j < NumOuputChannels; j++) + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory( + asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount ); + if( !deviceInfoArray ) { - long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; iinfo.deviceCount ]; + PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo; + + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + + deviceInfo->name = names[i]; + + deviceInfo->maxInputChannels = paAsioDriverInfo.numInputChannels; + deviceInfo->maxOutputChannels = paAsioDriverInfo.numOutputChannels; + + PA_DEBUG(("PaAsio_Initialize: inputChannels = %d\n", inputChannels )); + PA_DEBUG(("PaAsio_Initialize: outputChannels = %d\n", outputChannels )); + + + deviceInfo->defaultLowInputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultLowOutputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultHighInputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultHighOutputLatency = 0.; /* @todo IMPLEMENT ME */ + + deviceInfo->defaultSampleRate = 0.; + for( int j=0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j ) { - temp = *userBufPtr; - if (swap) temp = SwapLong(temp); - asioBufPtr[i] = ((float)temp) * (1.0f / MAX_INT32_FP); - userBufPtr += NumOuputChannels; + ASIOError asioError = ASIOCanSampleRate( defaultSampleRateSearchOrder_[j] ); + if( asioError != ASE_NoClock && asioError != ASE_NotPresent ){ + deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j]; + break; + } } + + 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; + } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Output_Int16_Int16(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset, int userFrameOffset,bool swap) -{ - long temp; - int i,j; - for (j= 0; j < NumOuputChannels; j++) + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyReadWrite, PaUtil_DummyReadWrite, PaUtil_DummyGetAvailable, PaUtil_DummyGetAvailable ); + + 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 ) { - short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; iallocations ); + PaUtil_DestroyAllocationGroup( asioHostApi->allocations ); } + + PaUtil_FreeMemory( asioHostApi ); + } + return result; } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Output_Int16_Int32(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap) + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) { - long temp; - int i,j; - - for (j= 0; j < NumOuputChannels; j++) - { - long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; iallocations ) + { + PaUtil_FreeAllAllocations( asioHostApi->allocations ); + PaUtil_DestroyAllocationGroup( asioHostApi->allocations ); + } + + PaUtil_FreeMemory( asioHostApi ); } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// MUST BE CHECKED -static void Output_Int16_Float32(ASIOBufferInfo* nativeBuffer, short *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap) + +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) { - long temp; - int i,j; - - for (j= 0; j < NumOuputChannels; j++) - { - float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - short *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; ichannelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support numInputChannels */ + if( numInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + numInputChannels = 0; + } + + if( outputParameters ) + { + numOutputChannels = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support numInputChannels */ + if( numOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + numOutputChannels = 0; + } + + /* + IMPLEMENT ME: + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + */ + + return paFormatIsSupported; } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Output_Int8_Int16(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap) -{ - long temp; - int i,j; - for (j= 0; j < NumOuputChannels; j++) - { - short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; inumOutputChannels; ++i ) + { + void *buffer = stream->asioBufferInfos[ i + stream->numInputChannels ].buffers[index]; - for (j= 0; j < NumOuputChannels; j++) - { - long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; iasioChannelInfos[ i + stream->numInputChannels ].type ); + + memset( buffer, 0, stream->framesPerHostCallback * bytesPerSample ); + } } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// MUST BE CHECKED -static void Output_Int8_Float32(ASIOBufferInfo* nativeBuffer, char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap) +static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames, + PaAsioDriverInfo *driverInfo ) { - long temp; - int i,j; - - for (j= 0; j < NumOuputChannels; j++) + unsigned long result; + + if( suggestedLatencyFrames == 0 ) + { + result = driverInfo->bufferPreferredSize; + } + else{ + if( suggestedLatencyFrames <= (unsigned long)driverInfo->bufferMinSize ) { - long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; ibufferMinSize; } -} + 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 ) + { + result = driverInfo->bufferPreferredSize; + } + else + { + /* modulo granularity */ + + result = suggestedLatencyFrames + + (driverInfo->bufferGranularity - + (suggestedLatencyFrames % driverInfo->bufferGranularity)); + 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 numInputChannels, numOutputChannels; + PaSampleFormat inputSampleFormat, outputSampleFormat; + PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + unsigned long suggestedInputLatencyFrames; + unsigned long suggestedOutputLatencyFrames; + const char *driverName; + ASIOError asioError; + int asioIsInitialized = 0; + int asioBuffersCreated = 0; + PaAsioDriverInfo driverInfo; + int i; + + /* unless we move to using lower level ASIO calls, we can only have + one device open at a time */ + if( asioHostApi->driverOpen ) + return paDeviceUnavailable; + + + + if( inputParameters ) + { + numInputChannels = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + suggestedInputLatencyFrames = inputParameters->suggestedLatency * sampleRate; + + driverName = asioHostApi->inheritedHostApiRep.deviceInfos[ inputParameters->device ]->name; + + /* 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 + { + numInputChannels = 0; + suggestedInputLatencyFrames = 0; + } + + if( outputParameters ) + { + numOutputChannels = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + suggestedOutputLatencyFrames = outputParameters->suggestedLatency; + + driverName = asioHostApi->inheritedHostApiRep.deviceInfos[ outputParameters->device ]->name; + + /* 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 + { + numOutputChannels = 0; + suggestedOutputLatencyFrames = 0; + } + + + if( inputParameters && outputParameters ) + { + /* full duplex ASIO stream must use the same device for input and output */ + + if( inputParameters->device != outputParameters->device ) + return paBadIODeviceCombination; + } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Output_IntU8_Int16(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap) -{ - long temp; - int i,j; + - for (j= 0; j < NumOuputChannels; j++) + /* 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( driverName, &driverInfo ); + if( result == paNoError ) + asioIsInitialized = 1; + else + goto error; + + /* check that input device can support numInputChannels */ + if( numInputChannels > 0 ) + { + if( numInputChannels > driverInfo.numInputChannels ) { - short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; i driverInfo.numOutputChannels ) { - long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; i suggestedOutputLatencyFrames ) + ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames), + &driverInfo ); + + /* + IMPLEMENT ME: + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + */ -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// MUST BE CHECKED + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ -static void Output_IntU8_Float32(ASIOBufferInfo* nativeBuffer, unsigned char *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap) -{ - long temp; - int i,j; - - for (j= 0; j < NumOuputChannels; j++) + + stream = (PaAsioStream*)PaUtil_AllocateMemory( sizeof(PaAsioStream) ); + if( !stream ) + { + result = paInsufficientMemory; + goto error; + } + + 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) * (numInputChannels + numOutputChannels) ); + if( !stream->asioBufferInfos ) + { + result = paInsufficientMemory; + goto error; + } + + + for( i=0; i < numInputChannels; ++i ) + { + ASIOBufferInfo *info = &stream->asioBufferInfos[i]; + + info->isInput = ASIOTrue; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = 0; + } + + for( i=0; i < numOutputChannels; ++i ){ + ASIOBufferInfo *info = &stream->asioBufferInfos[numInputChannels+i]; + + info->isInput = ASIOFalse; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = 0; + } + + asioError = ASIOCreateBuffers( stream->asioBufferInfos, numInputChannels+numOutputChannels, + framesPerHostBuffer, &asioCallbacks_ ); + if( asioError != ASE_OK ) + { + result = paUnanticipatedHostError; + PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); + goto error; + } + + asioBuffersCreated = 1; + + stream->asioChannelInfos = (ASIOChannelInfo*)PaUtil_AllocateMemory( + sizeof(ASIOChannelInfo) * (numInputChannels + numOutputChannels) ); + if( !stream->asioChannelInfos ) + { + result = paInsufficientMemory; + goto error; + } + + for( i=0; i < numInputChannels + numOutputChannels; ++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 ) { - float *asioBufPtr = &((float*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; - unsigned char *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; - for( i=0; ibufferPtrs = (void**)PaUtil_AllocateMemory( + 2 * sizeof(void*) * (numInputChannels + numOutputChannels) ); + if( !stream->bufferPtrs ) + { + result = paInsufficientMemory; + goto error; + } + + if( numInputChannels > 0 ) + { + stream->inputBufferPtrs[0] = stream-> bufferPtrs; + stream->inputBufferPtrs[1] = &stream->bufferPtrs[numInputChannels]; + + for( i=0; iinputBufferPtrs[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( numOutputChannels > 0 ) + { + stream->outputBufferPtrs[0] = &stream->bufferPtrs[numInputChannels*2]; + stream->outputBufferPtrs[1] = &stream->bufferPtrs[numInputChannels*2 + numOutputChannels]; + + for( i=0; ioutputBufferPtrs[0][i] = stream->asioBufferInfos[numInputChannels+i].buffers[0]; + stream->outputBufferPtrs[1][i] = stream->asioBufferInfos[numInputChannels+i].buffers[1]; } -} + } + else + { + stream->outputBufferPtrs[0] = 0; + stream->outputBufferPtrs[1] = 0; + } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Pa_ASIO_Clear_Output_16 (ASIOBufferInfo* nativeBuffer, long frames, long NumInputChannels, long NumOuputChannels, long index, long hostFrameOffset) -{ - int i,j; - for( j=0; jinputLatency, &stream->outputLatency ); -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Pa_ASIO_Clear_Output_32 (ASIOBufferInfo* nativeBuffer, long frames, long NumInputChannels, long NumOuputChannels, long index, long hostFrameOffset) -{ - int i,j; + stream->streamRepresentation.streamInfo.inputLatency = (double)stream->inputLatency / sampleRate; // seconds + stream->streamRepresentation.streamInfo.outputLatency = (double)stream->outputLatency / sampleRate; // seconds + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - for( j=0; jinputLatency, + (long)((stream->inputLatency*1000)/ sampleRate))); + PA_DEBUG(("PaAsio : OuputLatency = %ld latency = %ld msec \n", + stream->outputLatency, + (long)((stream->outputLatency*1000)/ sampleRate))); -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Pa_ASIO_Adaptor_Init() -{ - if (asioDriverInfo.past->past_FramesPerUserBuffer <= asioDriverInfo.past_FramesPerHostBuffer) { - asioDriverInfo.pahsc_hostOutputBufferFrameOffset = asioDriverInfo.pahsc_OutputBufferOffset; - asioDriverInfo.pahsc_userInputBufferFrameOffset = 0; // empty - asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty - }else { - asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0; // empty - asioDriverInfo.pahsc_userInputBufferFrameOffset = asioDriverInfo.pahsc_InputBufferOffset; - asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty - } -} + if( numInputChannels > 0 ) + { + /* FIXME: assume all channels use the same type for now */ + ASIOSampleType inputType = stream->asioChannelInfos[0].type; -//------------------------------------------------------------------------------------------------------------------------------------------------------- -// FIXME : optimization for Input only or output only modes (really necessary ??) -static void Pa_ASIO_Callback_Input( long index) -{ - internalPortAudioStream *past = asioDriverInfo.past; - long framesInputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer; // number of frames available into the host input buffer - long framesInputUserBuffer; // number of frames needed to complete the user input buffer - long framesOutputHostBuffer; // number of frames needed to complete the host output buffer - long framesOuputUserBuffer; // number of frames available into the user output buffer - long userResult; - long tmp; - - /* Fill host ASIO output with remaining frames in user output */ - framesOutputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer; - framesOuputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userOutputBufferFrameOffset; - tmp = min(framesOutputHostBuffer, framesOuputUserBuffer); - framesOutputHostBuffer -= tmp; - Pa_ASIO_Callback_Output(index,tmp); - - /* Available frames in hostInputBuffer */ - while (framesInputHostBuffer > 0) { - - /* Number of frames needed to complete an user input buffer */ - framesInputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userInputBufferFrameOffset; - - if (framesInputHostBuffer >= framesInputUserBuffer) { - - /* Convert ASIO input to user input */ - Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos, - past->past_InputBuffer, - asioDriverInfo.pahsc_NumInputChannels , - asioDriverInfo.pahsc_NumOutputChannels, - framesInputUserBuffer, - asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer, - asioDriverInfo.pahsc_userInputBufferFrameOffset, - asioDriverInfo.pahsc_channelInfos[0].type, - past->past_InputSampleFormat, - past->past_Flags, - index); - - /* Call PortAudio callback */ - userResult = asioDriverInfo.past->past_Callback(past->past_InputBuffer, past->past_OutputBuffer, - past->past_FramesPerUserBuffer,past->past_FrameCount,past->past_UserData ); - - /* User callback has asked us to stop in the middle of the host buffer */ - if( userResult != 0) { - - /* Put 0 in the end of the output buffer */ - Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, - asioDriverInfo.pahsc_channelInfos[0].type, - asioDriverInfo.pahsc_NumInputChannels , - asioDriverInfo.pahsc_NumOutputChannels, - index, - asioDriverInfo.pahsc_hostOutputBufferFrameOffset, - asioDriverInfo.past_FramesPerHostBuffer - asioDriverInfo.pahsc_hostOutputBufferFrameOffset); - - past->past_StopSoon = 1; - return; - } - - - /* Full user ouput buffer : write offset */ - asioDriverInfo.pahsc_userOutputBufferFrameOffset = 0; - - /* Empty user input buffer : read offset */ - asioDriverInfo.pahsc_userInputBufferFrameOffset = 0; - - /* Fill host ASIO output */ - tmp = min (past->past_FramesPerUserBuffer,framesOutputHostBuffer); - Pa_ASIO_Callback_Output(index,tmp); - - framesOutputHostBuffer -= tmp; - framesInputHostBuffer -= framesInputUserBuffer; - - }else { - - /* Convert ASIO input to user input */ - Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos, - past->past_InputBuffer, - asioDriverInfo.pahsc_NumInputChannels , - asioDriverInfo.pahsc_NumOutputChannels, - framesInputHostBuffer, - asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer, - asioDriverInfo.pahsc_userInputBufferFrameOffset, - asioDriverInfo.pahsc_channelInfos[0].type, - past->past_InputSampleFormat, - past->past_Flags, - index); - - /* Update pahsc_userInputBufferFrameOffset */ - asioDriverInfo.pahsc_userInputBufferFrameOffset += framesInputHostBuffer; - - /* Update framesInputHostBuffer */ - framesInputHostBuffer = 0; - } - } + hostInputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( inputType ); -} + SelectAsioToPaConverter( inputType, &stream->inputBufferConverter, &stream->inputShift ); + } + else + { + stream->inputBufferConverter = 0; + } -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Pa_ASIO_Callback_Output(long index, long framePerBuffer) -{ - internalPortAudioStream *past = asioDriverInfo.past; + if( numOutputChannels > 0 ) + { + /* FIXME: assume all channels use the same type for now */ + ASIOSampleType outputType = stream->asioChannelInfos[numInputChannels].type; - if (framePerBuffer > 0) { - - /* Convert user output to ASIO ouput */ - Pa_ASIO_Convert_Inter_Output (asioDriverInfo.bufferInfos, - past->past_OutputBuffer, - asioDriverInfo.pahsc_NumInputChannels, - asioDriverInfo.pahsc_NumOutputChannels, - framePerBuffer, - asioDriverInfo.pahsc_hostOutputBufferFrameOffset, - asioDriverInfo.pahsc_userOutputBufferFrameOffset, - asioDriverInfo.pahsc_channelInfos[0].type, - past->past_InputSampleFormat, - past->past_Flags, - index); - - /* Update hostOuputFrameOffset */ - asioDriverInfo.pahsc_hostOutputBufferFrameOffset += framePerBuffer; + hostOutputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( outputType ); - /* Update userOutputFrameOffset */ - asioDriverInfo.pahsc_userOutputBufferFrameOffset += framePerBuffer; - } -} -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Pa_ASIO_Callback_End() - { - /* Empty ASIO ouput : write offset */ - asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0; - } - -//------------------------------------------------------------------------------------------------------------------------------------------------------- -static void Pa_ASIO_Clear_User_Buffers() -{ - if( asioDriverInfo.past->past_InputBuffer != NULL ) - { - memset( asioDriverInfo.past->past_InputBuffer, 0, asioDriverInfo.past->past_InputBufferSize ); - } - if( asioDriverInfo.past->past_OutputBuffer != NULL ) - { - memset( asioDriverInfo.past->past_OutputBuffer, 0, asioDriverInfo.past->past_OutputBufferSize ); - } -} + SelectPaToAsioConverter( outputType, &stream->outputBufferConverter, &stream->outputShift ); + } + else + { + stream->outputBufferConverter = 0; + } -//------------------------------------------------------------------------------------------------------------------------------------------------------- - static void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer, - ASIOSampleType nativeFormat, - long NumInputChannels, - long NumOuputChannels, - long index, - long hostFrameOffset, - long frames) -{ - - switch (nativeFormat) { - - case ASIOSTInt16MSB: - case ASIOSTInt16LSB: - case ASIOSTInt32MSB16: - case ASIOSTInt32LSB16: - Pa_ASIO_Clear_Output_16(nativeBuffer, frames, NumInputChannels, NumOuputChannels, index, hostFrameOffset); - break; - - case ASIOSTFloat64MSB: - case ASIOSTFloat64LSB: - break; - - case ASIOSTFloat32MSB: - case ASIOSTFloat32LSB: - case ASIOSTInt32MSB: - case ASIOSTInt32LSB: - case ASIOSTInt32MSB18: - case ASIOSTInt32MSB20: - case ASIOSTInt32MSB24: - case ASIOSTInt32LSB18: - case ASIOSTInt32LSB20: - case ASIOSTInt32LSB24: - Pa_ASIO_Clear_Output_32(nativeBuffer, frames, NumInputChannels, NumOuputChannels, index, hostFrameOffset); - break; - - case ASIOSTInt24MSB: - case ASIOSTInt24LSB: - break; - - default: - break; - } -} + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + numInputChannels, inputSampleFormat, hostInputSampleFormat, + numOutputChannels, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, + framesPerHostBuffer, paUtilFixedHostBufferSize, + streamCallback, userData ); + if( result != paNoError ) + goto error; + stream->asioHostApi = asioHostApi; + stream->framesPerHostCallback = framesPerHostBuffer; -//--------------------------------------------------------------------------------------- -static void Pa_ASIO_Convert_Inter_Input( - ASIOBufferInfo* nativeBuffer, - void* inputBuffer, - long NumInputChannels, - long NumOuputChannels, - long framePerBuffer, - long hostFrameOffset, - long userFrameOffset, - ASIOSampleType nativeFormat, - PaSampleFormat paFormat, - PaStreamFlags flags, - long index) -{ - - if((NumInputChannels > 0) && (nativeBuffer != NULL)) - { - /* Convert from native format to PA format. */ - switch(paFormat) - { - case paFloat32: - { - float *inBufPtr = (float *) inputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Input_Int16_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset, swap); - break; - case ASIOSTInt16MSB: - Input_Int16_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap); - break; - case ASIOSTInt32LSB: - Input_Int32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap); - break; - case ASIOSTInt32MSB: - Input_Int32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap); - break; - case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap); - break; - case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - } - - break; - } - - case paInt32: - { - long *inBufPtr = (long *)inputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt16MSB: - Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTInt32LSB: - Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt32MSB: - Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - - } - break; - } - - case paInt16: - { - short *inBufPtr = (short *) inputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt16MSB: - Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTInt32LSB: - Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); - break; - case ASIOSTInt32MSB: - Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); - break; - case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - - } - break; - } - - case paInt8: - { - /* Convert 16 bit data to 8 bit chars */ - - char *inBufPtr = (char *) inputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap); - break; - case ASIOSTInt16MSB: - Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - case ASIOSTInt32LSB: - Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); - break; - case ASIOSTInt32MSB: - Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); - break; - case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - } - break; - } - - case paUInt8: - { - /* Convert 16 bit data to 8 bit unsigned chars */ - - unsigned char *inBufPtr = (unsigned char *)inputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); - break; - case ASIOSTInt16MSB: - Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - case ASIOSTInt32LSB: - Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap); - break; - case ASIOSTInt32MSB: - Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap); - break; - case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,!swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - - } - break; - } - - default: - break; - } - } -} + stream->numInputChannels = numInputChannels; + stream->numOutputChannels = numOutputChannels; + stream->postOutput = driverInfo.postOutput; + asioHostApi->driverOpen = 1; -//--------------------------------------------------------------------------------------- -static void Pa_ASIO_Convert_Inter_Output(ASIOBufferInfo* nativeBuffer, - void* outputBuffer, - long NumInputChannels, - long NumOuputChannels, - long framePerBuffer, - long hostFrameOffset, - long userFrameOffset, - ASIOSampleType nativeFormat, - PaSampleFormat paFormat, - PaStreamFlags flags, - long index) -{ - - if((NumOuputChannels > 0) && (nativeBuffer != NULL)) - { - /* Convert from PA format to native format */ - - switch(paFormat) - { - case paFloat32: - { - float *outBufPtr = (float *) outputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags, swap); - break; - case ASIOSTInt16MSB: - Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,!swap); - break; - case ASIOSTInt32LSB: - Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,swap); - break; - case ASIOSTInt32MSB: - Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - case ASIOSTFloat32LSB: - Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset,flags,swap); - break; - case ASIOSTFloat32MSB: - Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - } - break; - } - - case paInt32: - { - long *outBufPtr = (long *) outputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); - break; - case ASIOSTInt16MSB: - Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - case ASIOSTInt32LSB: - Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); - break; - case ASIOSTInt32MSB: - Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - case ASIOSTFloat32LSB: - Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); - break; - case ASIOSTFloat32MSB: - Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - } - break; - } - - case paInt16: - { - short *outBufPtr = (short *) outputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt16MSB: - Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTInt32LSB: - Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt32MSB: - Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTFloat32LSB: - Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTFloat32MSB: - Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - - } - break; - } - - - case paInt8: - { - char *outBufPtr = (char *) outputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt16MSB: - Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTInt32LSB: - Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt32MSB: - Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTFloat32LSB: - Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTFloat32MSB: - Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - } - break; - } - - case paUInt8: - { - unsigned char *outBufPtr = (unsigned char *) outputBuffer; - - switch (nativeFormat) { - case ASIOSTInt16LSB: - Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt16MSB: - Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTInt32LSB: - Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTInt32MSB: - Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - case ASIOSTFloat32LSB: - Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); - break; - case ASIOSTFloat32MSB: - Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); - break; - - case ASIOSTInt24LSB: // used for 20 bits as well - case ASIOSTInt24MSB: // used for 20 bits as well - - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - - // these are used for 32 bit data buffer, with different alignment of the data inside - // 32 bit PCI bus systems can more easily used with these - - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - - - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - DBUG(("Not yet implemented : please report the problem\n")); - break; - } - break; - } - - default: - break; - } - } + *s = (PaStream*)stream; -} + return result; +error: + if( stream ) + { + if( stream->asioBufferInfos ) + PaUtil_FreeMemory( stream->asioBufferInfos ); + if( stream->asioChannelInfos ) + PaUtil_FreeMemory( stream->asioChannelInfos ); -/* Load a ASIO driver corresponding to the required device */ -static PaError Pa_ASIO_loadDevice (long device) -{ - PaDeviceInfo * dev = &(sDevices[device].pad_Info); + if( stream->bufferPtrs ) + PaUtil_FreeMemory( stream->bufferPtrs ); - if (!Pa_ASIO_loadAsioDriver((char *) dev->name)) return paHostError; - if (ASIOInit(&asioDriverInfo.pahsc_driverInfo) != ASE_OK) return paHostError; - if (ASIOGetChannels(&asioDriverInfo.pahsc_NumInputChannels, &asioDriverInfo.pahsc_NumOutputChannels) != ASE_OK) return paHostError; - if (ASIOGetBufferSize(&asioDriverInfo.pahsc_minSize, &asioDriverInfo.pahsc_maxSize, &asioDriverInfo.pahsc_preferredSize, &asioDriverInfo.pahsc_granularity) != ASE_OK) return paHostError; - - if(ASIOOutputReady() == ASE_OK) - asioDriverInfo.pahsc_postOutput = true; - else - asioDriverInfo.pahsc_postOutput = false; - - return paNoError; + PaUtil_FreeMemory( stream ); + } + + if( asioBuffersCreated ) + ASIODisposeBuffers(); + + if( asioIsInitialized ) + ASIOExit(); + + return result; } -//--------------------------------------------------- -static int GetHighestBitPosition (unsigned long n) + +/* + When CloseStream() is called, the multi-api layer ensures that + the stream has already been stopped or aborted. +*/ +static PaError CloseStream( PaStream* s ) { - int pos = -1; - while( n != 0 ) - { - pos++; - n = n >> 1; - } - return pos; -} + PaError result = paNoError; + PaAsioStream *stream = (PaAsioStream*)s; -//------------------------------------------------------------------------------------------ -static int GetFirstMultiple(long min, long val ){ return ((min + val - 1) / val) * val; } + /* + IMPLEMENT ME: + - additional stream closing + cleanup + */ -//------------------------------------------------------------------------------------------ -static int GetFirstPossibleDivisor(long max, long val ) -{ - for (int i = 2; i < 20; i++) {if (((val%i) == 0) && ((val/i) <= max)) return (val/i); } - return val; -} + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); -//------------------------------------------------------------------------ -static int IsPowerOfTwo( unsigned long n ) { return ((n & (n-1)) == 0); } + stream->asioHostApi->driverOpen = 0; + PaUtil_FreeMemory( stream->asioBufferInfos ); + PaUtil_FreeMemory( stream->asioChannelInfos ); + PaUtil_FreeMemory( stream->bufferPtrs ); + PaUtil_FreeMemory( stream ); + + ASIODisposeBuffers(); + ASIOExit(); + + return result; +} -/******************************************************************* -* Determine size of native ASIO audio buffer size -* Input parameters : FramesPerUserBuffer, NumUserBuffers -* Output values : FramesPerHostBuffer, OutputBufferOffset or InputtBufferOffset -*/ -static PaError PaHost_CalcNumHostBuffers( internalPortAudioStream *past ) +static void bufferSwitch(long index, ASIOBool processNow) { - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - long requestedBufferSize; - long firstMultiple, firstDivisor; - - // Compute requestedBufferSize - if( past->past_NumUserBuffers < 1 ){ - requestedBufferSize = past->past_FramesPerUserBuffer; - }else{ - requestedBufferSize = past->past_NumUserBuffers * past->past_FramesPerUserBuffer; - } - - // Adjust FramesPerHostBuffer using requestedBufferSize, ASIO minSize and maxSize, - if (requestedBufferSize < asioDriverInfo.pahsc_minSize){ - - firstMultiple = GetFirstMultiple(asioDriverInfo.pahsc_minSize, requestedBufferSize); - - if (firstMultiple <= asioDriverInfo.pahsc_maxSize) - asioDriverInfo.past_FramesPerHostBuffer = firstMultiple; - else - asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_minSize; - - }else if (requestedBufferSize > asioDriverInfo.pahsc_maxSize){ - - firstDivisor = GetFirstPossibleDivisor(asioDriverInfo.pahsc_maxSize, requestedBufferSize); - - if ((firstDivisor >= asioDriverInfo.pahsc_minSize) && (firstDivisor <= asioDriverInfo.pahsc_maxSize)) - asioDriverInfo.past_FramesPerHostBuffer = firstDivisor; - else - asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_maxSize; - }else{ - asioDriverInfo.past_FramesPerHostBuffer = requestedBufferSize; - } +//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 - // If ASIO buffer size needs to be a power of two - if( asioDriverInfo.pahsc_granularity < 0 ){ - // Needs to be a power of two. + 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; - if( !IsPowerOfTwo( asioDriverInfo.past_FramesPerHostBuffer ) ) - { - int highestBit = GetHighestBitPosition(asioDriverInfo.past_FramesPerHostBuffer); - asioDriverInfo.past_FramesPerHostBuffer = 1 << (highestBit + 1); - } - } - - DBUG(("----------------------------------\n")); - DBUG(("PortAudio : minSize = %ld \n",asioDriverInfo.pahsc_minSize)); - DBUG(("PortAudio : preferredSize = %ld \n",asioDriverInfo.pahsc_preferredSize)); - DBUG(("PortAudio : maxSize = %ld \n",asioDriverInfo.pahsc_maxSize)); - DBUG(("PortAudio : granularity = %ld \n",asioDriverInfo.pahsc_granularity)); - DBUG(("PortAudio : User buffer size = %d\n", asioDriverInfo.past->past_FramesPerUserBuffer )); - DBUG(("PortAudio : ASIO buffer size = %d\n", asioDriverInfo.past_FramesPerHostBuffer )); - - if (asioDriverInfo.past_FramesPerHostBuffer > past->past_FramesPerUserBuffer){ - - // Computes the MINIMUM value of null frames shift for the output buffer alignement - asioDriverInfo.pahsc_OutputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer); - asioDriverInfo.pahsc_InputBufferOffset = 0; - DBUG(("PortAudio : Minimum BufferOffset for Output = %d\n", asioDriverInfo.pahsc_OutputBufferOffset)); - }else{ - - //Computes the MINIMUM value of null frames shift for the input buffer alignement - asioDriverInfo.pahsc_InputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer); - asioDriverInfo.pahsc_OutputBufferOffset = 0; - DBUG(("PortAudio : Minimum BufferOffset for Input = %d\n", asioDriverInfo.pahsc_InputBufferOffset)); - } - - return paNoError; + // Call the real callback + bufferSwitchTimeInfo( &timeInfo, index, processNow ); } -/***********************************************************************/ -int Pa_CountDevices() +// 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 processNow ) { - PaError err ; - - if( sNumDevices <= 0 ) - { - /* Force loading of ASIO drivers */ - err = Pa_ASIO_QueryDeviceInfo(sDevices); - if( err != paNoError ) goto error; - } - - return sNumDevices; + // 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. + + + (void) processNow; /* unused parameter: FIXME: the sdk implies that we shouldn't process now if this parameter is false */ + +#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 -error: - PaHost_Term(); - DBUG(("Pa_CountDevices: returns %d\n", err )); - return err; -} + if (timeInfo->timeInfo.flags & kSystemTimeValid) + asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime); + else + asioDriverInfo.nanoSeconds = 0; -/***********************************************************************/ -PaError PaHost_Init( void ) -{ - /* Have we already initialized the device info? */ - PaError err = (PaError) Pa_CountDevices(); - return ( err < 0 ) ? err : paNoError; -} + if (timeInfo->timeInfo.flags & kSamplePositionValid) + asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition); + else + asioDriverInfo.samples = 0; -/***********************************************************************/ -PaError PaHost_Term( void ) -{ - int i; - PaDeviceInfo *dev; - double *rates; - PaError result = paNoError; - - if (sNumDevices > 0) { - - /* Free allocated sample rate arrays and names*/ - for( i=0; isampleRates; - if ((rates != NULL)) PaHost_FreeFastMemory(rates, MAX_NUMSAMPLINGRATES * sizeof(double)); - dev->sampleRates = NULL; - if(dev->name != NULL) PaHost_FreeFastMemory((void *) dev->name, 32); - dev->name = NULL; - - } - - sNumDevices = 0; - - /* Dispose : if not done by Pa_CloseStream */ - if(ASIODisposeBuffers() != ASE_OK) result = paHostError; - if(ASIOExit() != ASE_OK) result = paHostError; - - /* remove the loaded ASIO driver */ - asioDrivers->removeCurrentDriver(); - } - - return result; -} + if (timeInfo->timeCode.flags & kTcValid) + asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples); + else + asioDriverInfo.tcSamples = 0; -/***********************************************************************/ -PaError PaHost_OpenStream( internalPortAudioStream *past ) -{ - PaError result = paNoError; - ASIOError err; - int32 device; + // 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 + + // Keep sample position + // FIXME: asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo; + + if( theAsioStream->stopProcessing || theAsioStream->abortProcessing ) { + + ZeroOutputBuffers( theAsioStream, index ); + + // Finally if the driver supports the ASIOOutputReady() optimization, + // do it here, all data are in place + if( theAsioStream->postOutput ) + ASIOOutputReady(); + + } + else + { + int i; - /* Check if a stream already runs */ - if (asioDriverInfo.past != NULL) return paHostError; - - /* Check the device number */ - if ((past->past_InputDeviceID != paNoDevice) - &&(past->past_OutputDeviceID != paNoDevice) - &&(past->past_InputDeviceID != past->past_OutputDeviceID)) + 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( theAsioStream->inputBufferConverter ) { - return paInvalidDeviceId; + for( i=0; inumInputChannels; i++ ) + { + theAsioStream->inputBufferConverter( theAsioStream->inputBufferPtrs[index][i], + theAsioStream->inputShift, theAsioStream->framesPerHostCallback ); + } } - /* Allocation */ - memset(&asioDriverInfo, 0, sizeof(PaHostSoundControl)); - past->past_DeviceData = (void*) &asioDriverInfo; - + PaUtil_BeginBufferProcessing( &theAsioStream->bufferProcessor, &paTimeInfo ); - /* FIXME */ - asioDriverInfo.past = past; - - /* load the ASIO device */ - device = (past->past_InputDeviceID < 0) ? past->past_OutputDeviceID : past->past_InputDeviceID; - result = Pa_ASIO_loadDevice(device); - if (result != paNoError) goto error; - - /* Check ASIO parameters and input parameters */ - if ((past->past_NumInputChannels > asioDriverInfo.pahsc_NumInputChannels) - || (past->past_NumOutputChannels > asioDriverInfo.pahsc_NumOutputChannels)) { - result = paInvalidChannelCount; - goto error; - } + PaUtil_SetInputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ ); + for( i=0; inumInputChannels; ++i ) + PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] ); + + PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ ); + for( i=0; inumOutputChannels; ++i ) + PaUtil_SetNonInterleavedOutputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->outputBufferPtrs[index][i] ); + + int callbackResult; + unsigned long framesProcessed = PaUtil_EndBufferProcessing( &theAsioStream->bufferProcessor, &callbackResult ); - /* Set sample rate */ - if (ASIOSetSampleRate(past->past_SampleRate) != ASE_OK) { - result = paInvalidSampleRate; - goto error; + if( theAsioStream->outputBufferConverter ) + { + for( i=0; inumOutputChannels; i++ ) + { + theAsioStream->outputBufferConverter( theAsioStream->outputBufferPtrs[index][i], + theAsioStream->outputShift, theAsioStream->framesPerHostCallback ); + } } - - /* if OK calc buffer size */ - result = PaHost_CalcNumHostBuffers( past ); - if (result != paNoError) goto error; - - - /* - Allocating input and output buffers number for the real past_NumInputChannels and past_NumOutputChannels - optimize the data transfer. - */ - - asioDriverInfo.pahsc_NumInputChannels = past->past_NumInputChannels; - asioDriverInfo.pahsc_NumOutputChannels = past->past_NumOutputChannels; - - /* Allocate ASIO buffers and callback*/ - err = Pa_ASIO_CreateBuffers(&asioDriverInfo, - asioDriverInfo.pahsc_NumInputChannels, - asioDriverInfo.pahsc_NumOutputChannels, - asioDriverInfo.past_FramesPerHostBuffer); - - if (err == ASE_OK) - return paNoError; - else if (err == ASE_NoMemory) - result = paInsufficientMemory; - else if (err == ASE_InvalidParameter) - result = paInvalidChannelCount; - else if (err == ASE_InvalidMode) - result = paBufferTooBig; - else - result = paHostError; - -error: - ASIOExit(); - return result; -} + PaUtil_EndCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer, framesProcessed ); -/***********************************************************************/ -PaError PaHost_CloseStream( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc; - PaError result = paNoError; + // 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 ) + { + /* IMPLEMENT ME - finish playback immediately */ + } + else + { + /* User callback has asked us to stop with paComplete or other non-zero value */ - if( past == NULL ) return paBadStreamPtr; - pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return paNoError; + /* IMPLEMENT ME - finish playback once currently queued audio has completed */ + } + } - #if PA_TRACE_START_STOP - AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); - #endif - - /* Dispose */ - if(ASIODisposeBuffers() != ASE_OK) result = paHostError; - if(ASIOExit() != ASE_OK) result = paHostError; - - /* Free data and device for output. */ - past->past_DeviceData = NULL; - asioDriverInfo.past = NULL; - - return result; + return 0L; } -/***********************************************************************/ -PaError PaHost_StartOutput( internalPortAudioStream *past ) + +static void sampleRateChanged(ASIOSampleRate sRate) { - /* Clear the index 0 host output buffer */ - Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, - asioDriverInfo.pahsc_channelInfos[0].type, - asioDriverInfo.pahsc_NumInputChannels, - asioDriverInfo.pahsc_NumOutputChannels, - 0, - 0, - asioDriverInfo.past_FramesPerHostBuffer); - - /* Clear the index 1 host output buffer */ - Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, - asioDriverInfo.pahsc_channelInfos[0].type, - asioDriverInfo.pahsc_NumInputChannels, - asioDriverInfo.pahsc_NumOutputChannels, - 1, - 0, - asioDriverInfo.past_FramesPerHostBuffer); - - Pa_ASIO_Clear_User_Buffers(); - - Pa_ASIO_Adaptor_Init(); + // 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. - return paNoError; + (void) sRate; /* unused parameter */ } -/***********************************************************************/ -PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +static long asioMessages(long selector, long value, void* message, double* opt) { - /* Nothing to do ?? */ - return paNoError; -} +// TAKEN FROM THE ASIO SDK + // currently the parameters "value", "message" and "opt" are not used. + long ret = 0; -/***********************************************************************/ -PaError PaHost_StartInput( internalPortAudioStream *past ) -{ - /* Nothing to do ?? */ - return paNoError; -} + (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->numOutputChannels > 0 ) + { + ZeroOutputBuffers( stream, 0 ); + ZeroOutputBuffers( stream, 1 ); + } + + stream->stopProcessing = 0; + stream->abortProcessing = 0; + + theAsioStream = stream; + asioError = ASIOStart(); + if( asioError != ASE_OK ) + { + theAsioStream = 0; + result = paUnanticipatedHostError; + PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); + } -/***********************************************************************/ -PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) -{ - /* Nothing to do */ - return paNoError; + return result; } -/***********************************************************************/ -PaError PaHost_StartEngine( internalPortAudioStream *past ) -{ - // TO DO : count of samples - past->past_IsActive = 1; - return (ASIOStart() == ASE_OK) ? paNoError : paHostError; -} -/***********************************************************************/ -PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +static PaError StopStream( PaStream *s ) { - // TO DO : count of samples - past->past_IsActive = 0; - return (ASIOStop() == ASE_OK) ? paNoError : paHostError; -} + PaError result = paNoError; + PaAsioStream *stream = (PaAsioStream*)s; + ASIOError asioError; -/***********************************************************************/ -// TO BE CHECKED -PaError PaHost_StreamActive( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc; - if( past == NULL ) return paBadStreamPtr; - pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return paInternalError; - return (PaError) past->past_IsActive; + stream->stopProcessing = 1; + stream->abortProcessing = 1; + + asioError = ASIOStop(); + if( asioError != ASE_OK ) + { + result = paUnanticipatedHostError; + PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); + } + + theAsioStream = 0; + + return result; } -/*************************************************************************/ -PaTimestamp Pa_StreamTime( PortAudioStream *stream ) -{ - PaHostSoundControl *pahsc; - internalPortAudioStream *past = (internalPortAudioStream *) stream; - if( past == NULL ) return paBadStreamPtr; - pahsc = (PaHostSoundControl *) past->past_DeviceData; - return pahsc->pahsc_NumFramesDone; -} -/************************************************************************* - * Allocate memory that can be accessed in real-time. - * This may need to be held in physical memory so that it is not - * paged to virtual memory. - * This call MUST be balanced with a call to PaHost_FreeFastMemory(). - */ -void *PaHost_AllocateFastMemory( long numBytes ) +static PaError AbortStream( PaStream *s ) { - #if MAC - void *addr = NewPtrClear( numBytes ); - if( (addr == NULL) || (MemError () != 0) ) return NULL; - - #if (CARBON_COMPATIBLE == 0) - if( HoldMemory( addr, numBytes ) != noErr ) - { - DisposePtr( (Ptr) addr ); - return NULL; - } - #endif - return addr; - #elif WINDOWS - void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */ - if( addr != NULL ) memset( addr, 0, numBytes ); - return addr; - #endif + /* ASIO doesn't provide Abort behavior, so just stop instead */ + return StopStream( s ); } -/************************************************************************* - * Free memory that could be accessed in real-time. - * This call MUST be balanced with a call to PaHost_AllocateFastMemory(). - */ -void PaHost_FreeFastMemory( void *addr, long numBytes ) + +static PaError IsStreamStopped( PaStream *s ) { - #if MAC - if( addr == NULL ) return; - #if CARBON_COMPATIBLE - (void) numBytes; - #else - UnholdMemory( addr, numBytes ); - #endif - DisposePtr( (Ptr) addr ); - #elif WINDOWS - if( addr != NULL ) free( addr ); - #endif + //PaAsioStream *stream = (PaAsioStream*)s; + (void) s; /* unused parameter */ + return theAsioStream == 0; } -/*************************************************************************/ -void Pa_Sleep( long msec ) +static PaError IsStreamActive( PaStream *s ) { - #if MAC - int32 sleepTime, endTime; - /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */ - sleepTime = ((msec * 60) + 999) / 1000; - if( sleepTime < 1 ) sleepTime = 1; - endTime = TickCount() + sleepTime; - do{ - DBUGX(("Sleep for %d ticks.\n", sleepTime )); - WaitNextEvent( 0, NULL, sleepTime, NULL ); /* Use this just to sleep without getting events. */ - sleepTime = endTime - TickCount(); - } while( sleepTime > 0 ); - #elif WINDOWS - Sleep( msec ); - #endif + //PaAsioStream *stream = (PaAsioStream*)s; + (void) s; /* unused parameter */ + return theAsioStream != 0; /* FIXME: currently there is no way to stop the stream from the callback */ } -/*************************************************************************/ -const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) -{ - if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL; - return &sDevices[id].pad_Info; -} -/*************************************************************************/ -PaDeviceID Pa_GetDefaultInputDeviceID( void ) +static PaTime GetStreamTime( PaStream *s ) { - return sDefaultInputDeviceID; + (void) s; /* unused parameter */ + return (double)timeGetTime() * .001; } -/*************************************************************************/ -PaDeviceID Pa_GetDefaultOutputDeviceID( void ) -{ - return sDefaultOutputDeviceID; -} -/*************************************************************************/ -int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) +static double GetStreamCpuLoad( PaStream* s ) { - // TO BE IMPLEMENTED : using the ASIOGetLatency call?? - return 2; -} + PaAsioStream *stream = (PaAsioStream*)s; -/*************************************************************************/ -int32 Pa_GetHostError( void ) -{ - int32 err = sPaHostError; - sPaHostError = 0; - return err; + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); } -#ifdef MAC +/* + 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 void Pa_StartUsageCalculation( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - UnsignedWide widePad; - if( pahsc == NULL ) return; -/* Query system timer for usage analysis and to prevent overuse of CPU. */ - Microseconds( &widePad ); - pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad ); -} -/**************************************************************************/ -static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +static PaError ReadStream( PaStream* s, + void *buffer, + unsigned long frames ) { - UnsignedWide widePad; - UInt64 CurrentCount; - long InsideCount; - long TotalCount; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return; -/* Measure CPU utilization during this callback. Note that this calculation -** assumes that we had the processor the whole time. -*/ -#define LOWPASS_COEFFICIENT_0 (0.9) -#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) - Microseconds( &widePad ); - CurrentCount = UnsignedWideToUInt64( widePad ); - if( past->past_IfLastExitValid ) - { - InsideCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_EntryCount); - TotalCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_LastExitCount); -/* Low pass filter the result because sometimes we get called several times in a row. -* That can cause the TotalCount to be very low which can cause the usage to appear -* unnaturally high. So we must filter numerator and denominator separately!!! -*/ - past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) + - (LOWPASS_COEFFICIENT_1 * InsideCount)); - past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) + - (LOWPASS_COEFFICIENT_1 * TotalCount)); - past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount; - } - pahsc->pahsc_LastExitCount = CurrentCount; - past->past_IfLastExitValid = 1; + PaAsioStream *stream = (PaAsioStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + (void) stream; /* unused parameters */ + (void) buffer; + (void) frames; + + return paNoError; } -#elif WINDOWS -/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ -static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +static PaError WriteStream( PaStream* s, + void *buffer, + unsigned long frames ) { - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return; -/* Query system timer for usage analysis and to prevent overuse of CPU. */ - QueryPerformanceCounter( &pahsc->pahsc_EntryCount ); + PaAsioStream *stream = (PaAsioStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + (void) stream; /* unused parameters */ + (void) buffer; + (void) frames; + + return paNoError; } -static void Pa_EndUsageCalculation( internalPortAudioStream *past ) + +static signed long GetStreamReadAvailable( PaStream* s ) { - LARGE_INTEGER CurrentCount = { 0, 0 }; - LONGLONG InsideCount; - LONGLONG TotalCount; -/* -** Measure CPU utilization during this callback. Note that this calculation -** assumes that we had the processor the whole time. -*/ -#define LOWPASS_COEFFICIENT_0 (0.9) -#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + PaAsioStream *stream = (PaAsioStream*)s; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - if( pahsc == NULL ) return; + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + (void) stream; /* unused parameter */ - if( QueryPerformanceCounter( &CurrentCount ) ) - { - if( past->past_IfLastExitValid ) - { - InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart; - TotalCount = CurrentCount.QuadPart - pahsc->pahsc_LastExitCount.QuadPart; -/* Low pass filter the result because sometimes we get called several times in a row. - * That can cause the TotalCount to be very low which can cause the usage to appear - * unnaturally high. So we must filter numerator and denominator separately!!! - */ - past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) + - (LOWPASS_COEFFICIENT_1 * InsideCount)); - past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) + - (LOWPASS_COEFFICIENT_1 * TotalCount)); - past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount; - } - pahsc->pahsc_LastExitCount = CurrentCount; - past->past_IfLastExitValid = 1; - } + return 0; } -#endif - +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaAsioStream *stream = (PaAsioStream*)s; + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + (void) stream; /* unused parameter */ + + return 0; +} diff --git a/pd/portaudio/pa_asio/pa_asio.h b/pd/portaudio/pa_asio/pa_asio.h new file mode 100644 index 00000000..c2775928 --- /dev/null +++ b/pd/portaudio/pa_asio/pa_asio.h @@ -0,0 +1,68 @@ +#ifndef PA_ASIO_H +#define PA_ASIO_H +/* + * + * 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. + * + */ + + +#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 minLatency 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 ); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PA_ASIO_H */ diff --git a/pd/portaudio/pa_asio/readme_asio_sdk_patch.txt b/pd/portaudio/pa_asio/readme_asio_sdk_patch.txt new file mode 100755 index 00000000..c0fdca7f --- /dev/null +++ b/pd/portaudio/pa_asio/readme_asio_sdk_patch.txt @@ -0,0 +1,25 @@ +There is a bug in the ASIO SDK that causes the Macintosh version to often fail during initialization. Here is a patch that you can apply. + +In codefragments.cpp replace getFrontProcessDirectory function with +the following one (GetFrontProcess replaced by GetCurrentProcess) + + +bool CodeFragments::getFrontProcessDirectory(void *specs) +{ + FSSpec *fss = (FSSpec *)specs; + ProcessInfoRec pif; + ProcessSerialNumber psn; + + memset(&psn,0,(long)sizeof(ProcessSerialNumber)); + // if(GetFrontProcess(&psn) == noErr) // wrong !!! + if(GetCurrentProcess(&psn) == noErr) // correct !!! + { + pif.processName = 0; + pif.processAppSpec = fss; + pif.processInfoLength = sizeof(ProcessInfoRec); + if(GetProcessInformation(&psn, &pif) == noErr) + return true; + } + return false; +} + diff --git a/pd/portaudio/pa_beos/PlaybackNode.cc b/pd/portaudio/pa_beos/PlaybackNode.cc new file mode 100644 index 00000000..41cbae34 --- /dev/null +++ b/pd/portaudio/pa_beos/PlaybackNode.cc @@ -0,0 +1,538 @@ +/* + * $Id: PlaybackNode.cc,v 1.1.1.1 2003-05-09 16:03:53 ggeiger Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * BeOS Media Kit Implementation by Joshua Haberman + * + * Copyright (c) 2001 Joshua Haberman + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * --- + * + * Significant portions of this file are based on sample code from Be. The + * Be Sample Code Licence follows: + * + * Copyright 1991-1999, Be Incorporated. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions, and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "PlaybackNode.h" + +#define PRINT(x) { printf x; fflush(stdout); } + +#ifdef DEBUG +#define DBUG(x) PRINT(x) +#else +#define DBUG(x) +#endif + + +PaPlaybackNode::PaPlaybackNode(uint32 channels, float frame_rate, uint32 frames_per_buffer, + PortAudioCallback* callback, void *user_data) : + BMediaNode("PortAudio input node"), + BBufferProducer(B_MEDIA_RAW_AUDIO), + BMediaEventLooper(), + mAborted(false), + mRunning(false), + mBufferGroup(NULL), + mDownstreamLatency(0), + mStartTime(0), + mCallback(callback), + mUserData(user_data), + mFramesPerBuffer(frames_per_buffer) +{ + DBUG(("Constructor called.\n")); + + mPreferredFormat.type = B_MEDIA_RAW_AUDIO; + mPreferredFormat.u.raw_audio.channel_count = channels; + mPreferredFormat.u.raw_audio.frame_rate = frame_rate; + mPreferredFormat.u.raw_audio.byte_order = + (B_HOST_IS_BENDIAN) ? B_MEDIA_BIG_ENDIAN : B_MEDIA_LITTLE_ENDIAN; + mPreferredFormat.u.raw_audio.buffer_size = + media_raw_audio_format::wildcard.buffer_size; + + mOutput.destination = media_destination::null; + mOutput.format = mPreferredFormat; + + /* The amount of time it takes for this node to produce a buffer when + * asked. Essentially, it is how long the user's callback takes to run. + * We set this to be the length of the sound data each buffer of the + * requested size can hold. */ + //mInternalLatency = (bigtime_t)(1000000 * frames_per_buffer / frame_rate); + + /* ACK! it seems that the mixer (at least on my machine) demands that IT + * specify the buffer size, so for now I'll just make a generic guess here */ + mInternalLatency = 1000000 / 20; +} + + + +PaPlaybackNode::~PaPlaybackNode() +{ + DBUG(("Destructor called.\n")); + Quit(); /* Stop the BMediaEventLooper thread */ +} + + +/************************* + * + * Local methods + * + */ + +bool PaPlaybackNode::IsRunning() +{ + return mRunning; +} + + +PaTimestamp PaPlaybackNode::GetStreamTime() +{ + BTimeSource *timeSource = TimeSource(); + PaTimestamp time = (timeSource->Now() - mStartTime) * + mPreferredFormat.u.raw_audio.frame_rate / 1000000; + return time; +} + + +void PaPlaybackNode::SetSampleFormat(PaSampleFormat inFormat, + PaSampleFormat outFormat) +{ + uint32 beOutFormat; + + switch(outFormat) + { + case paFloat32: + beOutFormat = media_raw_audio_format::B_AUDIO_FLOAT; + mOutputSampleWidth = 4; + break; + + case paInt16: + beOutFormat = media_raw_audio_format::B_AUDIO_SHORT; + mOutputSampleWidth = 2; + break; + + case paInt32: + beOutFormat = media_raw_audio_format::B_AUDIO_INT; + mOutputSampleWidth = 4; + break; + + case paInt8: + beOutFormat = media_raw_audio_format::B_AUDIO_CHAR; + mOutputSampleWidth = 1; + break; + + case paUInt8: + beOutFormat = media_raw_audio_format::B_AUDIO_UCHAR; + mOutputSampleWidth = 1; + break; + + case paInt24: + case paPackedInt24: + case paCustomFormat: + DBUG(("Unsupported output format: %x\n", outFormat)); + break; + + default: + DBUG(("Unknown output format: %x\n", outFormat)); + } + + mPreferredFormat.u.raw_audio.format = beOutFormat; + mFramesPerBuffer * mPreferredFormat.u.raw_audio.channel_count * mOutputSampleWidth; +} + +BBuffer *PaPlaybackNode::FillNextBuffer(bigtime_t time) +{ + /* Get a buffer from the buffer group */ + BBuffer *buf = mBufferGroup->RequestBuffer( + mOutput.format.u.raw_audio.buffer_size, BufferDuration()); + unsigned long frames = mOutput.format.u.raw_audio.buffer_size / + mOutputSampleWidth / mOutput.format.u.raw_audio.channel_count; + bigtime_t start_time; + int ret; + + if( !buf ) + { + DBUG(("Unable to allocate a buffer\n")); + return NULL; + } + + start_time = mStartTime + + (bigtime_t)((double)mSamplesSent / + (double)mOutput.format.u.raw_audio.frame_rate / + (double)mOutput.format.u.raw_audio.channel_count * + 1000000.0); + + /* Now call the user callback to get the data */ + ret = mCallback(NULL, /* Input buffer */ + buf->Data(), /* Output buffer */ + frames, /* Frames per buffer */ + mSamplesSent / mOutput.format.u.raw_audio.channel_count, /* timestamp */ + mUserData); + + if( ret ) + mAborted = true; + + media_header *hdr = buf->Header(); + + hdr->type = B_MEDIA_RAW_AUDIO; + hdr->size_used = mOutput.format.u.raw_audio.buffer_size; + hdr->time_source = TimeSource()->ID(); + hdr->start_time = start_time; + + return buf; +} + + + + +/************************* + * + * BMediaNode methods + * + */ + +BMediaAddOn *PaPlaybackNode::AddOn( int32 * ) const +{ + DBUG(("AddOn() called.\n")); + return NULL; /* we don't provide service to outside applications */ +} + + +status_t PaPlaybackNode::HandleMessage( int32 message, const void *data, + size_t size ) +{ + DBUG(("HandleMessage() called.\n")); + return B_ERROR; /* we don't define any custom messages */ +} + + + + +/************************* + * + * BMediaEventLooper methods + * + */ + +void PaPlaybackNode::NodeRegistered() +{ + DBUG(("NodeRegistered() called.\n")); + + /* Start the BMediaEventLooper thread */ + SetPriority(B_REAL_TIME_PRIORITY); + Run(); + + /* set up as much information about our output as we can */ + mOutput.source.port = ControlPort(); + mOutput.source.id = 0; + mOutput.node = Node(); + ::strcpy(mOutput.name, "PortAudio Playback"); +} + + +void PaPlaybackNode::HandleEvent( const media_timed_event *event, + bigtime_t lateness, bool realTimeEvent ) +{ + // DBUG(("HandleEvent() called.\n")); + status_t err; + + switch(event->type) + { + case BTimedEventQueue::B_START: + DBUG((" Handling a B_START event\n")); + if( RunState() != B_STARTED ) + { + mStartTime = event->event_time + EventLatency(); + mSamplesSent = 0; + mAborted = false; + mRunning = true; + media_timed_event firstEvent( mStartTime, + BTimedEventQueue::B_HANDLE_BUFFER ); + EventQueue()->AddEvent( firstEvent ); + } + break; + + case BTimedEventQueue::B_STOP: + DBUG((" Handling a B_STOP event\n")); + mRunning = false; + EventQueue()->FlushEvents( 0, BTimedEventQueue::B_ALWAYS, true, + BTimedEventQueue::B_HANDLE_BUFFER ); + break; + + case BTimedEventQueue::B_HANDLE_BUFFER: + //DBUG((" Handling a B_HANDLE_BUFFER event\n")); + + /* make sure we're started and connected */ + if( RunState() != BMediaEventLooper::B_STARTED || + mOutput.destination == media_destination::null ) + break; + + BBuffer *buffer = FillNextBuffer(event->event_time); + + /* make sure we weren't aborted while this routine was running. + * this can happen in one of two ways: either the callback returned + * nonzero (in which case mAborted is set in FillNextBuffer() ) or + * the client called AbortStream */ + if( mAborted ) + { + if( buffer ) + buffer->Recycle(); + Stop(0, true); + break; + } + + if( buffer ) + { + err = SendBuffer(buffer, mOutput.destination); + if( err != B_OK ) + buffer->Recycle(); + } + + mSamplesSent += mOutput.format.u.raw_audio.buffer_size / mOutputSampleWidth; + + /* Now schedule the next buffer event, so we can send another + * buffer when this one runs out. We calculate when it should + * happen by calculating when the data we just sent will finish + * playing. + * + * NOTE, however, that the event will actually get generated + * earlier than we specify, to account for the latency it will + * take to produce the buffer. It uses the latency value we + * specified in SetEventLatency() to determine just how early + * to generate it. */ + + /* totalPerformanceTime includes the time represented by the buffer + * we just sent */ + bigtime_t totalPerformanceTime = (bigtime_t)((double)mSamplesSent / + (double)mOutput.format.u.raw_audio.channel_count / + (double)mOutput.format.u.raw_audio.frame_rate * 1000000.0); + + bigtime_t nextEventTime = mStartTime + totalPerformanceTime; + + media_timed_event nextBufferEvent(nextEventTime, + BTimedEventQueue::B_HANDLE_BUFFER); + EventQueue()->AddEvent(nextBufferEvent); + + break; + + } +} + + + + +/************************* + * + * BBufferProducer methods + * + */ + +status_t PaPlaybackNode::FormatSuggestionRequested( media_type type, + int32 /*quality*/, media_format* format ) +{ + /* the caller wants to know this node's preferred format and provides + * a suggestion, asking if we support it */ + DBUG(("FormatSuggestionRequested() called.\n")); + + if(!format) + return B_BAD_VALUE; + + *format = mPreferredFormat; + + /* we only support raw audio (a wildcard is okay too) */ + if ( type == B_MEDIA_UNKNOWN_TYPE || type == B_MEDIA_RAW_AUDIO ) + return B_OK; + else + return B_MEDIA_BAD_FORMAT; +} + + +status_t PaPlaybackNode::FormatProposal( const media_source& output, + media_format* format ) +{ + /* This is similar to FormatSuggestionRequested(), but it is actually part + * of the negotiation process. We're given the opportunity to specify any + * properties that are wildcards (ie. properties that the other node doesn't + * care one way or another about) */ + DBUG(("FormatProposal() called.\n")); + + /* Make sure this proposal really applies to our output */ + if( output != mOutput.source ) + return B_MEDIA_BAD_SOURCE; + + /* We return two things: whether we support the proposed format, and our own + * preferred format */ + *format = mPreferredFormat; + + if( format->type == B_MEDIA_UNKNOWN_TYPE || format->type == B_MEDIA_RAW_AUDIO ) + return B_OK; + else + return B_MEDIA_BAD_FORMAT; +} + + +status_t PaPlaybackNode::FormatChangeRequested( const media_source& source, + const media_destination& destination, media_format* io_format, int32* ) +{ + /* we refuse to change formats, supporting only 1 */ + DBUG(("FormatChangeRequested() called.\n")); + + return B_ERROR; +} + + +status_t PaPlaybackNode::GetNextOutput( int32* cookie, media_output* out_output ) +{ + /* this is where we allow other to enumerate our outputs -- the cookie is + * an integer we can use to keep track of where we are in enumeration. */ + DBUG(("GetNextOutput() called.\n")); + + if( *cookie == 0 ) + { + *out_output = mOutput; + *cookie = 1; + return B_OK; + } + + return B_BAD_INDEX; +} + + +status_t PaPlaybackNode::DisposeOutputCookie( int32 cookie ) +{ + DBUG(("DisposeOutputCookie() called.\n")); + return B_OK; +} + + +void PaPlaybackNode::LateNoticeReceived( const media_source& what, + bigtime_t how_much, bigtime_t performance_time ) +{ + /* This function is called as notification that a buffer we sent wasn't + * received by the time we stamped it with -- it got there late. Basically, + * it means we underestimated our own latency, so we should increase it */ + DBUG(("LateNoticeReceived() called.\n")); + + if( what != mOutput.source ) + return; + + if( RunMode() == B_INCREASE_LATENCY ) + { + mInternalLatency += how_much; + SetEventLatency( mDownstreamLatency + mInternalLatency ); + DBUG(("Increasing latency to %Ld\n", mDownstreamLatency + mInternalLatency)); + } + else + DBUG(("I don't know what to do with this notice!")); +} + + +void PaPlaybackNode::EnableOutput( const media_source& what, bool enabled, + int32* ) +{ + DBUG(("EnableOutput() called.\n")); + /* stub -- we don't support this yet */ +} + + +status_t PaPlaybackNode::PrepareToConnect( const media_source& what, + const media_destination& where, media_format* format, + media_source* out_source, char* out_name ) +{ + /* the final stage of format negotiations. here we _must_ make specific any + * remaining wildcards */ + DBUG(("PrepareToConnect() called.\n")); + + /* make sure this really refers to our source */ + if( what != mOutput.source ) + return B_MEDIA_BAD_SOURCE; + + /* make sure we're not already connected */ + if( mOutput.destination != media_destination::null ) + return B_MEDIA_ALREADY_CONNECTED; + + if( format->type != B_MEDIA_RAW_AUDIO ) + return B_MEDIA_BAD_FORMAT; + + if( format->u.raw_audio.format != mPreferredFormat.u.raw_audio.format ) + return B_MEDIA_BAD_FORMAT; + + if( format->u.raw_audio.buffer_size == + media_raw_audio_format::wildcard.buffer_size ) + { + DBUG(("We were left to decide buffer size: choosing 2048")); + format->u.raw_audio.buffer_size = 2048; + } + else + DBUG(("Using consumer specified buffer size of %lu.\n", + format->u.raw_audio.buffer_size)); + + /* Reserve the connection, return the information */ + mOutput.destination = where; + mOutput.format = *format; + *out_source = mOutput.source; + strncpy( out_name, mOutput.name, B_MEDIA_NAME_LENGTH ); + + return B_OK; +} + + +void PaPlaybackNode::Connect(status_t error, const media_source& source, + const media_destination& destination, const media_format& format, char* io_name) +{ + DBUG(("Connect() called.\n")); + diff --git a/pd/portaudio/pa_beos/PlaybackNode.h b/pd/portaudio/pa_beos/PlaybackNode.h new file mode 100644 index 00000000..db978a59 --- /dev/null +++ b/pd/portaudio/pa_beos/PlaybackNode.h @@ -0,0 +1,108 @@ +/* + * $Id: PlaybackNode.h,v 1.1.1.1 2003-05-09 16:03:53 ggeiger Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * BeOS Media Kit Implementation by Joshua Haberman + * + * Copyright (c) 2001 Joshua Haberman + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include + +#include "portaudio.h" + +class PaPlaybackNode : + public BBufferProducer, + public BMediaEventLooper +{ + +public: + PaPlaybackNode( uint32 channels, float frame_rate, uint32 frames_per_buffer, + PortAudioCallback *callback, void *user_data ); + ~PaPlaybackNode(); + + + /* Local methods ******************************************/ + + BBuffer *FillNextBuffer(bigtime_t time); + void SetSampleFormat(PaSampleFormat inFormat, PaSampleFormat outFormat); + bool IsRunning(); + PaTimestamp GetStreamTime(); + + /* BMediaNode methods *************************************/ + + BMediaAddOn* AddOn( int32 * ) const; + status_t HandleMessage( int32 message, const void *data, size_t size ); + + /* BMediaEventLooper methods ******************************/ + + void HandleEvent( const media_timed_event *event, bigtime_t lateness, + bool realTimeEvent ); + void NodeRegistered(); + + /* BBufferProducer methods ********************************/ + + status_t FormatSuggestionRequested( media_type type, int32 quality, + media_format* format ); + status_t FormatProposal( const media_source& output, media_format* format ); + status_t FormatChangeRequested( const media_source& source, + const media_destination& destination, media_format* io_format, int32* ); + + status_t GetNextOutput( int32* cookie, media_output* out_output ); + status_t DisposeOutputCookie( int32 cookie ); + + void LateNoticeReceived( const media_source& what, bigtime_t how_much, + bigtime_t performance_time ); + void EnableOutput( const media_source& what, bool enabled, int32* _deprecated_ ); + + status_t PrepareToConnect( const media_source& what, + const media_destination& where, media_format* format, + media_source* out_source, char* out_name ); + void Connect(status_t error, const media_source& source, + const media_destination& destination, const media_format& format, + char* io_name); + void Disconnect(const media_source& what, const media_destination& where); + + status_t SetBufferGroup(const media_source& for_source, BBufferGroup* newGroup); + + bool mAborted; + +private: + media_output mOutput; + media_format mPreferredFormat; + uint32 mOutputSampleWidth, mFramesPerBuffer; + BBufferGroup *mBufferGroup; + bigtime_t mDownstreamLatency, mInternalLatency, mStartTime; + uint64 mSamplesSent; + PortAudioCallback *mCallback; + void *mUserData; + bool mRunning; + +}; + diff --git a/pd/portaudio/pa_beos/pa_beos_mk.cc b/pd/portaudio/pa_beos/pa_beos_mk.cc new file mode 100644 index 00000000..3307a2ff --- /dev/null +++ b/pd/portaudio/pa_beos/pa_beos_mk.cc @@ -0,0 +1,441 @@ +/* + * $Id: pa_beos_mk.cc,v 1.1.1.1 2003-05-09 16:03:53 ggeiger Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * BeOS Media Kit Implementation by Joshua Haberman + * + * Copyright (c) 2001 Joshua Haberman + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "portaudio.h" +#include "pa_host.h" + +#include "PlaybackNode.h" + +#define PRINT(x) { printf x; fflush(stdout); } + +#ifdef DEBUG +#define DBUG(x) PRINT(x) +#else +#define DBUG(x) +#endif + +typedef struct PaHostSoundControl +{ + /* These members are common to all modes of operation */ + media_node pahsc_TimeSource; /* the sound card's DAC. */ + media_format pahsc_Format; + + /* These methods are specific to playing mode */ + media_node pahsc_OutputNode; /* output to the mixer */ + media_node pahsc_InputNode; /* reads data from user callback -- PA specific */ + + media_input pahsc_MixerInput; /* input jack on the soundcard's mixer. */ + media_output pahsc_PaOutput; /* output jack from the PA node */ + + PaPlaybackNode *pahsc_InputNodeInstance; + +} +PaHostSoundControl; + +/*************************************************************************/ +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + /* stub */ + return 0; +} + +/*************************************************************************/ +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + /* stub */ + return 0; +} + +/*************************************************************************/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + /* stub */ + return NULL; +} + +/*************************************************************************/ +int Pa_CountDevices() +{ + /* stub */ + return 1; +} + +/*************************************************************************/ +PaError PaHost_Init( void ) +{ + /* we have to create this in order to use BMediaRoster. I hope it doesn't + * cause problems */ + be_app = new BApplication("application/x-vnd.portaudio-app"); + + return paNoError; +} + +PaError PaHost_Term( void ) +{ + delete be_app; + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_StreamActive( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData; + DBUG(("IsRunning returning: %s\n", + pahsc->pahsc_InputNodeInstance->IsRunning() ? "true" : "false")); + + return (PaError)pahsc->pahsc_InputNodeInstance->IsRunning(); +} + +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) +{ + return paNoError; +} + +/*************************************************************************/ +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +{ + return paNoError; +} + + +/*************************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + bigtime_t very_soon, start_latency; + status_t err; + BMediaRoster *roster = BMediaRoster::Roster(&err); + PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData; + + /* for some reason, err indicates an error (though nothing it wrong) + * when the DBUG macro in pa_lib.c is enabled. It's reproducably + * linked. Weird. */ + if( !roster /* || err != B_OK */ ) + { + DBUG(("No media server! err=%d, roster=%x\n", err, roster)); + return paHostError; + } + + /* tell the node when to start -- since there aren't any other nodes + * starting that we have to wait for, just tell it to start now + */ + + BTimeSource *timeSource = roster->MakeTimeSourceFor(pahsc->pahsc_TimeSource); + very_soon = timeSource->PerformanceTimeFor( BTimeSource::RealTime() ); + timeSource->Release(); + + /* Add the latency of starting the network of nodes */ + err = roster->GetStartLatencyFor( pahsc->pahsc_TimeSource, &start_latency ); + very_soon += start_latency; + + err = roster->StartNode( pahsc->pahsc_InputNode, very_soon ); + /* No need to start the mixer -- it's always running */ + + return paNoError; +} + + +/*************************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData; + BMediaRoster *roster = BMediaRoster::Roster(); + + if( !roster ) + { + DBUG(("No media roster!\n")); + return paHostError; + } + + if( !pahsc ) + return paHostError; + + /* this crashes, and I don't know why yet */ + // if( abort ) + // pahsc->pahsc_InputNodeInstance->mAborted = true; + + roster->StopNode(pahsc->pahsc_InputNode, 0, /* immediate = */ true); + + return paNoError; +} + + +/*************************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *past ) +{ + status_t err; + BMediaRoster *roster = BMediaRoster::Roster(&err); + PaHostSoundControl *pahsc; + + /* Allocate and initialize host data. */ + pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); + if( pahsc == NULL ) + { + goto error; + } + memset( pahsc, 0, sizeof(PaHostSoundControl) ); + past->past_DeviceData = (void *) pahsc; + + if( !roster /* || err != B_OK */ ) + { + /* no media server! */ + DBUG(("No media server.\n")); + goto error; + } + + if ( past->past_NumInputChannels > 0 && past->past_NumOutputChannels > 0 ) + { + /* filter -- not implemented yet */ + goto error; + } + else if ( past->past_NumInputChannels > 0 ) + { + /* recorder -- not implemented yet */ + goto error; + } + else + { + /* player ****************************************************************/ + + status_t err; + int32 num; + + /* First we need to create the three components (like components in a stereo + * system). The mixer component is our interface to the sound card, data + * we write there will get played. The BePA_InputNode component is the node + * which represents communication with the PA client (it is what calls the + * client's callbacks). The time source component is the sound card's DAC, + * which allows us to slave the other components to it instead of the system + * clock. */ + err = roster->GetAudioMixer( &pahsc->pahsc_OutputNode ); + if( err != B_OK ) + { + DBUG(("Couldn't get default mixer.\n")); + goto error; + } + + err = roster->GetTimeSource( &pahsc->pahsc_TimeSource ); + if( err != B_OK ) + { + DBUG(("Couldn't get time source.\n")); + goto error; + } + + pahsc->pahsc_InputNodeInstance = new PaPlaybackNode(2, 44100, + past->past_FramesPerUserBuffer, past->past_Callback, past->past_UserData ); + pahsc->pahsc_InputNodeInstance->SetSampleFormat(0, + past->past_OutputSampleFormat); + err = roster->RegisterNode( pahsc->pahsc_InputNodeInstance ); + if( err != B_OK ) + { + DBUG(("Unable to register node.\n")); + goto error; + } + + roster->GetNodeFor( pahsc->pahsc_InputNodeInstance->Node().node, + &pahsc->pahsc_InputNode ); + if( err != B_OK ) + { + DBUG(("Unable to get input node.\n")); + goto error; + } + + /* Now we have three components (nodes) sitting next to each other. The + * next step is to look at them and find their inputs and outputs so we can + * wire them together. */ + err = roster->GetFreeInputsFor( pahsc->pahsc_OutputNode, + &pahsc->pahsc_MixerInput, 1, &num, B_MEDIA_RAW_AUDIO ); + if( err != B_OK || num < 1 ) + { + DBUG(("Couldn't get the mixer input.\n")); + goto error; + } + + err = roster->GetFreeOutputsFor( pahsc->pahsc_InputNode, + &pahsc->pahsc_PaOutput, 1, &num, B_MEDIA_RAW_AUDIO ); + if( err != B_OK || num < 1 ) + { + DBUG(("Couldn't get PortAudio output.\n")); + goto error; + } + + + /* We've found the input and output -- the final step is to run a wire + * between them so they are connected. */ + + /* try to make the mixer input adapt to what PA sends it */ + pahsc->pahsc_Format = pahsc->pahsc_PaOutput.format; + roster->Connect( pahsc->pahsc_PaOutput.source, + pahsc->pahsc_MixerInput.destination, &pahsc->pahsc_Format, + &pahsc->pahsc_PaOutput, &pahsc->pahsc_MixerInput ); + + + /* Actually, there's one final step -- tell them all to sync to the + * sound card's DAC */ + roster->SetTimeSourceFor( pahsc->pahsc_InputNode.node, + pahsc->pahsc_TimeSource.node ); + roster->SetTimeSourceFor( pahsc->pahsc_OutputNode.node, + pahsc->pahsc_TimeSource.node ); + + } + + return paNoError; + +error: + PaHost_CloseStream( past ); + return paHostError; +} + +/*************************************************************************/ +PaError PaHost_CloseStream( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData; + status_t err; + BMediaRoster *roster = BMediaRoster::Roster(&err); + + if( !roster ) + { + DBUG(("Couldn't get media roster\n")); + return paHostError; + } + + if( !pahsc ) + return paHostError; + + /* Disconnect all the connections we made when opening the stream */ + + roster->Disconnect(pahsc->pahsc_InputNode.node, pahsc->pahsc_PaOutput.source, + pahsc->pahsc_OutputNode.node, pahsc->pahsc_MixerInput.destination); + + DBUG(("Calling ReleaseNode()")); + roster->ReleaseNode(pahsc->pahsc_InputNode); + + /* deleting the node shouldn't be necessary -- it is reference counted, and will + * delete itself when its references drop to zero. the call to ReleaseNode() + * above should decrease its reference count */ + pahsc->pahsc_InputNodeInstance = NULL; + + return paNoError; +} + +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + internalPortAudioStream *past = (internalPortAudioStream *) stream; + PaHostSoundControl *pahsc = (PaHostSoundControl *)past->past_DeviceData; + + return pahsc->pahsc_InputNodeInstance->GetStreamTime(); +} + +/*************************************************************************/ +void Pa_Sleep( long msec ) +{ + /* snooze() takes microseconds */ + snooze( msec * 1000 ); +} + +/************************************************************************* + * Allocate memory that can be accessed in real-time. + * This may need to be held in physical memory so that it is not + * paged to virtual memory. + * This call MUST be balanced with a call to PaHost_FreeFastMemory(). + * Memory will be set to zero. + */ +void *PaHost_AllocateFastMemory( long numBytes ) +{ + /* BeOS supports non-pagable memory through pools -- a pool is an area + * of physical memory that is locked. It would be best to pre-allocate + * that pool and then hand out memory from it, but we don't know in + * advance how much we'll need. So for now, we'll allocate a pool + * for every request we get, storing a pointer to the pool at the + * beginning of the allocated memory */ + rtm_pool *pool; + void *addr; + long size = numBytes + sizeof(rtm_pool *); + static int counter = 0; + char pool_name[100]; + + /* Every pool needs a unique name. */ + sprintf(pool_name, "PaPoolNumber%d", counter++); + + if( rtm_create_pool( &pool, size, pool_name ) != B_OK ) + return 0; + + addr = rtm_alloc( pool, size ); + if( addr == NULL ) + return 0; + + memset( addr, 0, numBytes ); + *((rtm_pool **)addr) = pool; // store the pointer to the pool + addr = (rtm_pool **)addr + 1; // and return the next location in memory + + return addr; +} + +/************************************************************************* + * Free memory that could be accessed in real-time. + * This call MUST be balanced with a call to PaHost_AllocateFastMemory(). + */ +void PaHost_FreeFastMemory( void *addr, long numBytes ) +{ + rtm_pool *pool; + + if( addr == NULL ) + return; + + addr = (rtm_pool **)addr - 1; + pool = *((rtm_pool **)addr); + + rtm_free( addr ); + rtm_delete_pool( pool ); +} diff --git a/pd/portaudio/pa_common/pa_allocation.c b/pd/portaudio/pa_common/pa_allocation.c new file mode 100644 index 00000000..5eefeabb --- /dev/null +++ b/pd/portaudio/pa_common/pa_allocation.c @@ -0,0 +1,217 @@ +/* + * Id: + * Portable Audio I/O Library allocation group implementation + * memory allocation group for tracking allocation groups + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#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 . The remaining + links will have NULL buffer members, and each link will point to + the next link except the last, which will point to +*/ +static struct PaUtilAllocationGroupLink *AllocateLinks( long count, + struct PaUtilAllocationGroupLink *nextBlock, + struct PaUtilAllocationGroupLink *nextSpare ) +{ + struct PaUtilAllocationGroupLink *result; + int i; + + result = 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; ilinkCount = 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..b906c14b --- /dev/null +++ b/pd/portaudio/pa_common/pa_allocation.h @@ -0,0 +1,92 @@ +#ifndef PA_ALLOCATION_H +#define PA_ALLOCATION_H +/* + * Id: + * Portable Audio I/O Library allocation context header + * memory allocation context for tracking allocation groups + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/** @file + 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 +*/ + + + +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_allocation.o b/pd/portaudio/pa_common/pa_allocation.o new file mode 100644 index 00000000..43ac4148 Binary files /dev/null and b/pd/portaudio/pa_common/pa_allocation.o differ diff --git a/pd/portaudio/pa_common/pa_converters.c b/pd/portaudio/pa_common/pa_converters.c new file mode 100644 index 00000000..39e5e47f --- /dev/null +++ b/pd/portaudio/pa_common/pa_converters.c @@ -0,0 +1,1653 @@ +/* + * $Id: pa_converters.c,v 1.1.2.13 2003/02/28 01:49:59 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 + + @todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither, + Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24, + Int32_To_Int24_Dither, Int32_To_Int16, Int32_To_Int16_Dither, Int32_To_Int8, + Int32_To_Int8_Dither, Int32_To_UInt8, Int32_To_UInt8_Dither, Int24_To_Int32, + Int24_To_Int16, Int24_To_Int16_Dither, Int24_To_Int8, Int24_To_Int8_Dither, + Int24_To_UInt8, Int24_To_UInt8_Dither, Int16_To_Int32, Int16_To_Int24, + Int16_To_Int8, Int16_To_Int8_Dither, Int16_To_UInt8, Int16_To_UInt8_Dither, + Int8_To_Int32, Int8_To_Int24, Int8_To_Int16, Int8_To_UInt8, UInt8_To_Int32, + UInt8_To_Int24, UInt8_To_Int16, UInt8_To_Int8 + + @todo review the converters marked REVIEW: Float32_To_Int32, + Float32_To_Int32_Dither, Float32_To_Int32_Clip, Float32_To_Int32_DitherClip +*/ + + +#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 */ + double scaled = *src * 0x7FFFFFFF; + *dest = (signed long) scaled; + + 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 */ + 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; + + 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 */ + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + *dest = (signed long) scaled; + + 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 */ + 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; + + + 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-- ) + { + + short samp = (short) (*src * (32767.0f)); + *dest = samp; + + 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; + *dest = (signed short) dithered; + + 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-- ) + { + + long samp = (signed long) (*src * (32767.0f)); + 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 ); + *dest = (signed short) samp; + + 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 = (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 ) +{ + (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_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-- ) + { + + /* IMPLEMENT ME */ + + 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; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + + /* IMPLEMENT ME */ + + 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-- ) + { + + /* IMPLEMENT ME */ + + 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; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + + /* IMPLEMENT ME */ + + 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-- ) + { + + /* IMPLEMENT ME */ + + 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 = (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 ) +{ + (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_Int16( + 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_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 ) +{ + (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_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 ) +{ + (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_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-- ) + { + + /* IMPLEMENT ME */ + + 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 ) +{ + (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_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-- ) + { + + /* IMPLEMENT ME */ + + 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-- ) + { + + /* IMPLEMENT ME */ + + 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-- ) + { + + /* IMPLEMENT ME */ + + 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-- ) + { + + /* IMPLEMENT ME */ + + 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-- ) + { + + /* IMPLEMENT ME */ + + 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-- ) + { + + /* IMPLEMENT ME */ + + 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-- ) + { + + /* IMPLEMENT ME */ + + 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 ) +{ + (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 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-- ) + { + + /* IMPLEMENT ME */ + + 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; + float *dest = (float*)destinationBuffer; + (void)ditherGenerator; /* unused parameter */ + + while( count-- ) + { + + /* IMPLEMENT ME */ + + 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 */ diff --git a/pd/portaudio/pa_common/pa_converters.h b/pd/portaudio/pa_common/pa_converters.h new file mode 100644 index 00000000..8fa0fda7 --- /dev/null +++ b/pd/portaudio/pa_common/pa_converters.h @@ -0,0 +1,197 @@ +#ifndef PA_CONVERTERS_H +#define PA_CONVERTERS_H +/* + * $Id: pa_converters.h,v 1.1.2.7 2002/10/22 08:58:17 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. + */ + +#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 PaUtil_Converter 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 ); + + +/* low level functions and data structures which may be used for + substituting additional 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; + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_CONVERTERS_H */ diff --git a/pd/portaudio/pa_common/pa_converters.o b/pd/portaudio/pa_common/pa_converters.o new file mode 100644 index 00000000..3baede8e Binary files /dev/null and b/pd/portaudio/pa_common/pa_converters.o differ diff --git a/pd/portaudio/pa_common/pa_cpuload.c b/pd/portaudio/pa_common/pa_cpuload.c new file mode 100644 index 00000000..bce82aff --- /dev/null +++ b/pd/portaudio/pa_common/pa_cpuload.c @@ -0,0 +1,79 @@ +/* + * $Id: pa_cpuload.c,v 1.1.2.9 2002/10/22 08:58:17 rossbencina Exp $ + * Portable Audio I/O Library CPU Load measurement functions + * Portable CPU load measurement facility. + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 2002 Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pa_cpuload.h" + +#include + +#include "pa_util.h" /* for PaUtil_GetTime() */ + + +void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate ) +{ + assert( sampleRate > 0 ); + + measurer->samplingPeriod = 1. / sampleRate; +} + + +void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer ) +{ + measurer->measurementStartTime = PaUtil_GetTime(); +} + + +void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed ) +{ + double measurementEndTime, secondsFor100Percent, measuredLoad; + + if( framesProcessed > 0 ){ + measurementEndTime = PaUtil_GetTime(); + + assert( framesProcessed > 0 ); + secondsFor100Percent = framesProcessed * measurer->samplingPeriod; + + measuredLoad = (measurementEndTime - measurer->measurementStartTime) / secondsFor100Percent; + + /* Low pass filter the calculated CPU load to reduce jitter using a simple IIR low pass filter. */ +#define LOWPASS_COEFFICIENT_0 (0.9) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + + measurer->averageLoad = (LOWPASS_COEFFICIENT_0 * measurer->averageLoad) + + (LOWPASS_COEFFICIENT_1 * measuredLoad); + } +} + + +double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer ) +{ + return measurer->averageLoad; +} diff --git a/pd/portaudio/pa_common/pa_cpuload.h b/pd/portaudio/pa_common/pa_cpuload.h new file mode 100644 index 00000000..28c4c29a --- /dev/null +++ b/pd/portaudio/pa_common/pa_cpuload.h @@ -0,0 +1,56 @@ +#ifndef PA_CPULOAD_H +#define PA_CPULOAD_H +/* + * $Id: pa_cpuload.h,v 1.1.2.8 2002/10/22 08:58:17 rossbencina Exp $ + * Portable Audio I/O Library CPU Load measurement functions + * Portable CPU load measurement facility. + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 2002 Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +typedef struct { + double samplingPeriod; + double measurementStartTime; + double averageLoad; +} PaUtilCpuLoadMeasurer; /** @todo need better name than measurer */ + +void PaUtil_InitializeCpuLoadMeasurer( PaUtilCpuLoadMeasurer* measurer, double sampleRate ); +void PaUtil_BeginCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer ); +void PaUtil_EndCpuLoadMeasurement( PaUtilCpuLoadMeasurer* measurer, unsigned long framesProcessed ); +double PaUtil_GetCpuLoad( PaUtilCpuLoadMeasurer* measurer ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_CPULOAD_H */ diff --git a/pd/portaudio/pa_common/pa_cpuload.o b/pd/portaudio/pa_common/pa_cpuload.o new file mode 100644 index 00000000..bc599f45 Binary files /dev/null and b/pd/portaudio/pa_common/pa_cpuload.o differ diff --git a/pd/portaudio/pa_common/pa_dither.c b/pd/portaudio/pa_common/pa_dither.c new file mode 100644 index 00000000..10f43e69 --- /dev/null +++ b/pd/portaudio/pa_common/pa_dither.c @@ -0,0 +1,91 @@ +/* + * $Id: pa_dither.c,v 1.1.2.3 2002/06/16 13:11:02 rossbencina Exp $ + * Portable Audio I/O Library triangular dither generator + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pa_dither.h" + +#define PA_DITHER_BITS_ (15) + + +void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *state ) +{ + state->previous = 0; + state->randSeed1 = 22222; + state->randSeed2 = 5555555; +} + + +signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *state ) +{ + signed long current, highPass; + + /* Generate two random numbers. */ + state->randSeed1 = (state->randSeed1 * 196314165) + 907633515; + state->randSeed2 = (state->randSeed2 * 196314165) + 907633515; + + /* Generate triangular distribution about 0. + * Shift before adding to prevent overflow which would skew the distribution. + * Also shift an extra bit for the high pass filter. + */ +#define DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1) + current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) + + (((signed long)state->randSeed2)>>DITHER_SHIFT_); + + /* High pass filter to reduce audibility. */ + highPass = current - state->previous; + state->previous = current; + return highPass; +} + +/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */ +#define PA_FLOAT_DITHER_SCALE_ (1.0f / ((1<randSeed1 = (state->randSeed1 * 196314165) + 907633515; + state->randSeed2 = (state->randSeed2 * 196314165) + 907633515; + + /* Generate triangular distribution about 0. + * Shift before adding to prevent overflow which would skew the distribution. + * Also shift an extra bit for the high pass filter. + */ +#define DITHER_SHIFT_ ((32 - PA_DITHER_BITS_) + 1) + current = (((signed long)state->randSeed1)>>DITHER_SHIFT_) + + (((signed long)state->randSeed2)>>DITHER_SHIFT_); + + /* High pass filter to reduce audibility. */ + highPass = current - state->previous; + state->previous = current; + return ((float)highPass) * const_float_dither_scale_; +} diff --git a/pd/portaudio/pa_common/pa_dither.h b/pd/portaudio/pa_common/pa_dither.h new file mode 100644 index 00000000..97116f0f --- /dev/null +++ b/pd/portaudio/pa_common/pa_dither.h @@ -0,0 +1,189 @@ +#ifndef PA_DITHER_H +#define PA_DITHER_H +/* + * $Id: pa_dither.h,v 1.1.2.2 2002/06/05 22:37:03 rossb Exp $ + * Portable Audio I/O Library triangular dither generator + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +typedef struct PaUtilTriangularDitherGenerator{ + unsigned long previous; + unsigned long randSeed1; + unsigned long randSeed2; +} PaUtilTriangularDitherGenerator; +/**< State needed to generate a dither signal */ + + +void PaUtil_InitializeTriangularDitherState( PaUtilTriangularDitherGenerator *ditherState ); +/**< Initialize dither state */ + +signed long PaUtil_Generate16BitTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); +/**< + Calculate 2 LSB dither signal with a triangular distribution. + Ranged for adding to a 1 bit right-shifted 32 bit integer + prior to >>15. eg: +
    +    signed long in = *
    +    signed long dither = PaUtil_Generate16BitTriangularDither( ditherState );
    +    signed short out = (signed short)(((in>>1) + dither) >> 15);
    +
    + @return + A signed long with a range of +32767 to -32768 +*/ + + +float PaUtil_GenerateFloatTriangularDither( PaUtilTriangularDitherGenerator *ditherState ); +/**< + Calculate 2 LSB dither signal with a triangular distribution. + Ranged for adding to a pre-scaled float. +
    +    float in = *
    +    float dither = PaUtil_GenerateFloatTriangularDither( ditherState );
    +    // use smaller scaler to prevent overflow when we add the dither
    +    signed short out = (signed short)(in*(32766.0f) + dither );
    +
    + @return + A float with a range of -2.0 to +1.99999. +*/ + + + +/* +The following alternate dither algorithms are known... +*/ + +/*Noise shaped dither (March 2000) +------------------- + +This is a simple implementation of highpass triangular-PDF dither with +2nd-order noise shaping, for use when truncating floating point audio +data to fixed point. + +The noise shaping lowers the noise floor by 11dB below 5kHz (@ 44100Hz +sample rate) compared to triangular-PDF dither. The code below assumes +input data is in the range +1 to -1 and doesn't check for overloads! + +To save time when generating dither for multiple channels you can do +things like this: r3=(r1 & 0x7F)<<8; instead of calling rand() again. + + + + int r1, r2; //rectangular-PDF random numbers + float s1, s2; //error feedback buffers + float s = 0.5f; //set to 0.0f for no noise shaping + float w = pow(2.0,bits-1); //word length (usually bits=16) + float wi= 1.0f/w; + float d = wi / RAND_MAX; //dither amplitude (2 lsb) + float o = wi * 0.5f; //remove dc offset + float in, tmp; + int out; + + +//for each sample... + + r2=r1; //can make HP-TRI dither by + r1=rand(); //subtracting previous rand() + + in += s * (s1 + s1 - s2); //error feedback + tmp = in + o + d * (float)(r1 - r2); //dc offset and dither + + out = (int)(w * tmp); //truncate downwards + if(tmp<0.0f) out--; //this is faster than floor() + + s2 = s1; + s1 = in - wi * (float)out; //error + + + +-- +paul.kellett@maxim.abel.co.uk +http://www.maxim.abel.co.uk +*/ + + +/* +16-to-8-bit first-order dither + +Type : First order error feedforward dithering code +References : Posted by Jon Watte + +Notes : +This is about as simple a dithering algorithm as you can implement, but it's +likely to sound better than just truncating to N bits. + +Note that you might not want to carry forward the full difference for infinity. +It's probably likely that the worst performance hit comes from the saturation +conditionals, which can be avoided with appropriate instructions on many DSPs +and integer SIMD type instructions, or CMOV. + +Last, if sound quality is paramount (such as when going from > 16 bits to 16 +bits) you probably want to use a higher-order dither function found elsewhere +on this site. + + +Code : +// This code will down-convert and dither a 16-bit signed short +// mono signal into an 8-bit unsigned char signal, using a first +// order forward-feeding error term dither. + +#define uchar unsigned char + +void dither_one_channel_16_to_8( short * input, uchar * output, int count, int * memory ) +{ + int m = *memory; + while( count-- > 0 ) { + int i = *input++; + i += m; + int j = i + 32768 - 128; + uchar o; + if( j < 0 ) { + o = 0; + } + else if( j > 65535 ) { + o = 255; + } + else { + o = (uchar)((j>>8)&0xff); + } + m = ((j-32768+128)-i); + *output++ = o; + } + *memory = m; +} +*/ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_DITHER_H */ diff --git a/pd/portaudio/pa_common/pa_dither.o b/pd/portaudio/pa_common/pa_dither.o new file mode 100644 index 00000000..fc7a4bb4 Binary files /dev/null and b/pd/portaudio/pa_common/pa_dither.o differ diff --git a/pd/portaudio/pa_common/pa_endianness.h b/pd/portaudio/pa_common/pa_endianness.h new file mode 100644 index 00000000..aaccaf75 --- /dev/null +++ b/pd/portaudio/pa_common/pa_endianness.h @@ -0,0 +1,108 @@ +#ifndef PA_ENDIANNESS_H +#define PA_ENDIANNESS_H +/* + * $Id: pa_endianness.h,v 1.1.2.1 2003/02/28 01:49:59 rossbencina Exp $ + * Portable Audio I/O Library current platform endianness macros + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/** @file + Arrange for either the PA_LITTLE_ENDIAN or PA_BIG_ENDIAN preprocessor symbols + to be defined. The one that is defined reflects the endianness of the target + platform and may be used to implement conditional compilation of byte-order + dependent code. + + If either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN is defined already, then no attempt + is made to override that setting. This may be useful if you have a better way + of determining the platform's endianness. The autoconf mechanism uses this for + example. + + A PA_VALIDATE_ENDIANNESS macro is provided to compare the compile time + and runtime endiannes and raise an assertion if they don't match. +*/ + + +#if defined(PA_LITTLE_ENDIAN) || defined(PA_BIG_ENDIAN) + /* endianness define has been set externally, such as by autoconf */ + + #if defined(PA_LITTLE_ENDIAN) && defined(PA_BIG_ENDIAN) + #error both PA_LITTLE_ENDIAN and PA_BIG_ENDIAN have been defined externally to pa_endianness.h - only one endianness at a time please + #endif + +#else + /* endianness define has not been set externally */ + + /* set PA_LITTLE_ENDIAN or PA_BIG_ENDIAN by testing well known platform specific defines */ + + #ifdef WIN32 + + #define PA_LITTLE_ENDIAN /* win32, assume intel byte order */ + + #else + +#endif + + #if !defined(PA_LITTLE_ENDIAN) && !defined(PA_BIG_ENDIAN) + /* + If the following error is raised, you either need to modify the code above + to automatically determine the endianness from other symbols defined on your + platform, or define either PA_LITTLE_ENDIAN or PA_BIG_ENDIAN externally. + */ + #error pa_endianness.h was unable to automatically determine the endianness of the target platform + #endif + +#endif + +/* PA_VALIDATE_ENDIANNESS compares the compile time and runtime endianness, + and raises an assertion if they don't match. must be included in + the context in which this macro is used. +*/ +#if defined(PA_LITTLE_ENDIAN) + #define PA_VALIDATE_ENDIANNESS \ + { \ + const long nativeOne = 1; \ + assert( "compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 1 ); \ + } +#elif defined(PA_BIG_ENDIAN) + #define PA_VALIDATE_ENDIANNESS \ + { \ + const long nativeOne = 1; \ + assert( "compile time and runtime endianness don't match" && (((char *)&nativeOne)[0]) == 0 ); \ + } +#endif + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_ENDIANNESS_H */ diff --git a/pd/portaudio/pa_common/pa_front.c b/pd/portaudio/pa_common/pa_front.c new file mode 100644 index 00000000..27293b20 --- /dev/null +++ b/pd/portaudio/pa_common/pa_front.c @@ -0,0 +1,1884 @@ +/* + * $Id: pa_front.c,v 1.1.2.36 2003/02/28 01:49:59 rossbencina Exp $ + * Portable Audio I/O Library Multi-Host API front end + * Validate function parameters and manage multiple host APIs. + * + * 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. + */ + +/* doxygen index page */ +/** @mainpage + +PortAudio is an open-source cross-platform ‘C’ library for audio input +and output. It is designed to simplify the porting of audio applications +between various platforms, and also to simplify the development of audio +software in general by hiding the complexities of device interfacing. + +See the PortAudio website for further information http://www.portaudio.com/ + +This documentation pertains to PortAudio V19, API version 2.0 which is +currently under development. API version 2.0 differs in a number of ways from +previous versions, please consult the enhancement proposals for further details: +http://www.portaudio.com/docs/proposals/index.html + +This documentation is under construction. Things you might be interested in +include: + +- The PortAudio API 2.0 documented in portaudio.h + +- The possibly incomplete and totally unorganised Todo List +*/ + +#include +#include +#include +#include +#include /* needed by PA_VALIDATE_ENDIANNESS */ + +#include "portaudio.h" +#include "pa_util.h" +#include "pa_endianness.h" +#include "pa_hostapi.h" +#include "pa_stream.h" + +#include "pa_trace.h" + + +#define PA_VERSION_ 1899 +#define PA_VERSION_TEXT_ "PortAudio V19-devel" + + + +/* #define PA_LOG_API_CALLS */ + +/* + The basic format for log messages is as follows: + + - entry (void function): + + "FunctionName called.\n" + + - entry (non void function): + + "FunctionName called:\n" + "\tParam1Type param1: param1Value\n" + "\tParam2Type param2: param2Value\n" (etc...) + + + - exit (no return value) + + "FunctionName returned.\n" + + - exit (simple return value) + + "FunctionName returned:\n" + "\tReturnType: returnValue\n\n" + + if the return type is an error code, the error text is displayed in () + + if the return type is not an error code, but has taken a special value + because an error occurred, then the reason for the error is shown in [] + + if the return type is a struct ptr, the struct is dumped. + + see the code for more detailed examples +*/ + +int Pa_GetVersion( void ) +{ + return PA_VERSION_; +} + + +const char* Pa_GetVersionText( void ) +{ + return PA_VERSION_TEXT_; +} + + + +#define PA_LAST_HOST_ERROR_TEXT_LENGTH_ 1024 + +static char lastHostErrorText_[ PA_LAST_HOST_ERROR_TEXT_LENGTH_ + 1 ] = {0}; + +static PaHostErrorInfo lastHostErrorInfo_ = { -1, 0, lastHostErrorText_ }; + + +void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode, + const char *errorText ) +{ + lastHostErrorInfo_.hostApiType = hostApiType; + lastHostErrorInfo_.errorCode = errorCode; + + strncpy( lastHostErrorText_, errorText, PA_LAST_HOST_ERROR_TEXT_LENGTH_ ); +} + + +void PaUtil_DebugPrint( const char *format, ... ) +{ + va_list ap; + + va_start( ap, format ); + vfprintf( stderr, format, ap ); + va_end( ap ); + + fflush( stderr ); +} + + +static PaUtilHostApiRepresentation **hostApis_ = 0; +static int hostApisCount_ = 0; +static int initializationCount_ = 0; +static int deviceCount_ = 0; + +PaUtilStreamRepresentation *firstOpenStream_ = NULL; + + +#define PA_IS_INITIALISED_ (initializationCount_ != 0) + + +static int CountHostApiInitializers( void ) +{ + int result = 0; + + while( paHostApiInitializers[ result ] != 0 ) + ++result; + return result; +} + + +static void TerminateHostApis( void ) +{ + /* terminate in reverse order from initialization */ + + while( hostApisCount_ > 0 ) + { + --hostApisCount_; + hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] ); + } + hostApisCount_ = 0; + deviceCount_ = 0; + + if( hostApis_ != 0 ) + PaUtil_FreeMemory( hostApis_ ); + hostApis_ = 0; +} + + +static PaError InitializeHostApis( void ) +{ + PaError result = paNoError; + int i, initializerCount, baseDeviceIndex; + + initializerCount = CountHostApiInitializers(); + + hostApis_ = PaUtil_AllocateMemory( sizeof(PaUtilHostApiRepresentation*) * initializerCount ); + if( !hostApis_ ) + { + result = paInsufficientMemory; + goto error; + } + + hostApisCount_ = 0; + deviceCount_ = 0; + baseDeviceIndex = 0; + + for( i=0; i< initializerCount; ++i ) + { + hostApis_[hostApisCount_] = NULL; + result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ ); + if( result != paNoError ) + goto error; + + if( hostApis_[hostApisCount_] ) + { + + hostApis_[hostApisCount_]->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex; + + if( hostApis_[hostApisCount_]->info.defaultInputDevice != paNoDevice ) + hostApis_[hostApisCount_]->info.defaultInputDevice += baseDeviceIndex; + + if( hostApis_[hostApisCount_]->info.defaultOutputDevice != paNoDevice ) + hostApis_[hostApisCount_]->info.defaultOutputDevice += baseDeviceIndex; + + baseDeviceIndex += hostApis_[hostApisCount_]->info.deviceCount; + deviceCount_ += hostApis_[hostApisCount_]->info.deviceCount; + + ++hostApisCount_; + } + } + + return result; + +error: + TerminateHostApis(); + return result; +} + + +/* + FindHostApi() finds the index of the host api to which + belongs and returns it. if is + non-null, the host specific device index is returned in it. + returns -1 if is out of range. + +*/ +static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex ) +{ + int i=0; + + if( !PA_IS_INITIALISED_ ) + return -1; + + if( device < 0 ) + return -1; + + while( i < hostApisCount_ + && device >= hostApis_[i]->info.deviceCount ) + { + + device -= hostApis_[i]->info.deviceCount; + ++i; + } + + if( i >= hostApisCount_ ) + return -1; + + if( hostSpecificDeviceIndex ) + *hostSpecificDeviceIndex = device; + + return i; +} + + +static void AddOpenStream( PaStream* stream ) +{ + ((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_; + firstOpenStream_ = (PaUtilStreamRepresentation*)stream; +} + + +static void RemoveOpenStream( PaStream* stream ) +{ + PaUtilStreamRepresentation *previous = NULL; + PaUtilStreamRepresentation *current = firstOpenStream_; + + while( current != NULL ) + { + if( ((PaStream*)current) == stream ) + { + if( previous == NULL ) + { + firstOpenStream_ = current->nextOpenStream; + } + else + { + previous->nextOpenStream = current->nextOpenStream; + } + return; + } + else + { + previous = current; + current = current->nextOpenStream; + } + } +} + + +static void CloseOpenStreams( void ) +{ + /* we call Pa_CloseStream() here to ensure that the same destruction + logic is used for automatically closed streams */ + + while( firstOpenStream_ != NULL ) + Pa_CloseStream( firstOpenStream_ ); +} + + +PaError Pa_Initialize( void ) +{ + PaError result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint( "Pa_Initialize called.\n" ); +#endif + + if( PA_IS_INITIALISED_ ) + { + ++initializationCount_; + result = paNoError; + } + else + { + PA_VALIDATE_ENDIANNESS; + + PaUtil_InitializeClock(); + PaUtil_ResetTraceMessages(); + + result = InitializeHostApis(); + if( result == paNoError ) + ++initializationCount_; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint( "Pa_Initialize returned:\n" ); + PaUtil_DebugPrint( "\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_Terminate( void ) +{ + PaError result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_Terminate called.\n" ); +#endif + + if( PA_IS_INITIALISED_ ) + { + if( --initializationCount_ == 0 ) + { + CloseOpenStreams(); + + TerminateHostApis(); + + PaUtil_DumpTraceMessages(); + } + result = paNoError; + } + else + { + result= paNotInitialized; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_Terminate returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void ) +{ + return &lastHostErrorInfo_; +} + + +const char *Pa_GetErrorText( PaError errorNumber ) +{ + const char *result; + + switch( errorNumber ) + { + case paNoError: result = "Success"; break; + case paNotInitialized: result = "PortAudio not initialized"; break; + /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */ + case paUnanticipatedHostError: result = "Unanticipated host error"; break; + case paInvalidChannelCount: result = "Invalid number of channels"; break; + case paInvalidSampleRate: result = "Invalid sample rate"; break; + case paInvalidDevice: result = "Invalid device"; break; + case paInvalidFlag: result = "Invalid flag"; break; + case paSampleFormatNotSupported: result = "Sample format not supported"; break; + case paBadIODeviceCombination: result = "Illegal combination of I/O devices"; break; + case paInsufficientMemory: result = "Insufficient memory"; break; + case paBufferTooBig: result = "Buffer too big"; break; + case paBufferTooSmall: result = "Buffer too small"; break; + case paNullCallback: result = "No callback routine specified"; break; + case paBadStreamPtr: result = "Invalid stream pointer"; break; + case paTimedOut: result = "Wait timed out"; break; + case paInternalError: result = "Internal PortAudio error"; break; + case paDeviceUnavailable: result = "Device unavailable"; break; + case paIncompatibleHostApiSpecificStreamInfo: result = "Incompatible host API specific stream info"; break; + case paStreamIsStopped: result = "Stream is stopped"; break; + case paStreamIsNotStopped: result = "Stream is not stopped"; break; + default: result = "Illegal error number"; break; + } + return result; +} + + +PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type ) +{ + PaHostApiIndex result; + int i; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex called:\n" ); + PaUtil_DebugPrint("\PaHostApiTypeId type: %d\n", type ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + + result = -1; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiIndex: -1 [ PortAudio not initialized ]\n\n" ); +#endif + + } + else + { + result = -1; + + for( i=0; i < hostApisCount_; ++i ) + { + if( hostApis_[i]->info.type == type ) + { + result = i; + break; + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiTypeIdToHostApiIndex returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiIndex: %d\n\n", result ); +#endif + } + + return result; +} + + +PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **hostApi, + PaHostApiTypeId type ) +{ + PaError result; + int i; + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + } + else + { + result = paInternalError; /* @todo should return host API not found */ + + for( i=0; i < hostApisCount_; ++i ) + { + if( hostApis_[i]->info.type == type ) + { + *hostApi = hostApis_[i]; + result = paNoError; + break; + } + } + } + + return result; +} + + +PaError PaUtil_DeviceIndexToHostApiDeviceIndex( + PaDeviceIndex *hostApiDevice, PaDeviceIndex device, struct PaUtilHostApiRepresentation *hostApi ) +{ + PaError result; + PaDeviceIndex x; + + x = device - hostApi->privatePaFrontInfo.baseDeviceIndex; + + if( x < 0 || x >= hostApi->info.deviceCount ) + { + result = paInvalidDevice; + } + else + { + *hostApiDevice = x; + result = paNoError; + } + + return result; +} + + +PaHostApiIndex Pa_CountHostApis( void ) +{ + int result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CountHostApis called.\n" ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CountHostApis returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; + } + else + { + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CountHostApis returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", hostApisCount_ ); +#endif + + return hostApisCount_; + } +} + + +PaHostApiIndex Pa_GetDefaultHostApi( void ) +{ + int result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultHostApi called.\n" ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; + } + else + { + result = paDefaultHostApiIndex; + + /* internal consistency check: make sure that the default host api + index is within range */ + + if( result < 0 || result >= hostApisCount_ ) + { + result = paInternalError; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + } + else + { +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultHostApi returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiIndex %d\n\n", result ); +#endif + } + + return result; + } +} + + +const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi ) +{ + PaHostApiInfo *info; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiInfo called:\n" ); + PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + info = NULL; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n\n" ); +#endif + + } + else if( hostApi < 0 || hostApi >= hostApisCount_ ) + { + info = NULL; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiInfo*: NULL [ hostApi out of range ]\n\n" ); +#endif + + } + else + { + info = &hostApis_[hostApi]->info; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetHostApiInfo returned:\n" ); + PaUtil_DebugPrint("\tPaHostApiInfo*: 0x%p\n", info ); + PaUtil_DebugPrint("\t{" ); + PaUtil_DebugPrint("\t\tint structVersion: %d\n", info->structVersion ); + PaUtil_DebugPrint("\t\tPaHostApiTypeId type: %d\n", info->type ); + PaUtil_DebugPrint("\t\tconst char *name: %s\n\n", info->name ); + PaUtil_DebugPrint("\t}\n\n" ); +#endif + + } + + return info; +} + + +PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int hostApiDeviceIndex ) +{ + PaDeviceIndex result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex called:\n" ); + PaUtil_DebugPrint("\tPaHostApiIndex hostApi: %d\n", hostApi ); + PaUtil_DebugPrint("\tint hostApiDeviceIndex: %d\n", hostApiDeviceIndex ); +#endif + + + if( !PA_IS_INITIALISED_ ) + { + result = paNoDevice; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: paNoDevice [ PortAudio not initialized ]\n\n" ); +#endif + + } + else + { + if( hostApi < 0 || hostApi >= hostApisCount_ ) + { + result = paNoDevice; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: paNoDevice [ hostApi out of range ]\n\n" ); +#endif + + } + else + { + if( hostApiDeviceIndex < 0 || + hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount ) + { + result = paNoDevice; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: paNoDevice [ hostApiDeviceIndex out of range ]\n\n" ); +#endif + + } + else + { + result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_HostApiDeviceIndexToPaDeviceIndex returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); +#endif + } + } + } + + return result; +} + + +PaDeviceIndex Pa_CountDevices( void ) +{ + PaDeviceIndex result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CountDevices called.\n" ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CountDevices returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: 0 [ PortAudio not initialized ]\n\n" ); +#endif + + } + else + { + result = deviceCount_; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CountDevices returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); +#endif + + } + + return result; +} + + +PaDeviceIndex Pa_GetDefaultInputDevice( void ) +{ + PaHostApiIndex hostApi; + PaDeviceIndex result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultInputDevice called.\n" ); +#endif + + hostApi = Pa_GetDefaultHostApi(); + if( hostApi < 0 ) + { + result = paNoDevice; + } + else + { + result = hostApis_[hostApi]->info.defaultInputDevice; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultInputDevice returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); +#endif + + return result; +} + + +PaDeviceIndex Pa_GetDefaultOutputDevice( void ) +{ + PaHostApiIndex hostApi; + PaDeviceIndex result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultOutputDevice called.\n" ); +#endif + + hostApi = Pa_GetDefaultHostApi(); + if( hostApi < 0 ) + { + result = paNoDevice; + } + else + { + result = hostApis_[hostApi]->info.defaultOutputDevice; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDefaultOutputDevice returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex: %d\n\n", result ); +#endif + + return result; +} + + +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device ) +{ + int hostSpecificDeviceIndex; + int hostApiIndex = FindHostApi( device, &hostSpecificDeviceIndex ); + PaDeviceInfo *result; + + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDeviceInfo called:\n" ); + PaUtil_DebugPrint("\tPaDeviceIndex device: %d\n", device ); +#endif + + if( hostApiIndex < 0 ) + { + result = NULL; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceInfo* NULL [ invalid device index ]\n\n" ); +#endif + + } + else + { + result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ]; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetDeviceInfo returned:\n" ); + PaUtil_DebugPrint("\tPaDeviceInfo*: 0x%p:\n", result ); + PaUtil_DebugPrint("\t{" ); + + PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion ); + PaUtil_DebugPrint("\t\tconst char *name: %s\n", result->name ); + PaUtil_DebugPrint("\t\tPaHostApiIndex hostApi: %d\n", result->hostApi ); + PaUtil_DebugPrint("\t\tint maxInputChannels: %d\n", result->maxInputChannels ); + PaUtil_DebugPrint("\t\tint maxOutputChannels: %d\n", result->maxOutputChannels ); + PaUtil_DebugPrint("\t}\n\n" ); +#endif + + } + + return result; +} + + +/* + SampleFormatIsValid() returns 1 if sampleFormat is a sample format + defined in portaudio.h, or 0 otherwise. +*/ +static int SampleFormatIsValid( PaSampleFormat format ) +{ + switch( format & ~paNonInterleaved ) + { + case paFloat32: return 1; + case paInt16: return 1; + case paInt32: return 1; + case paInt24: return 1; + case paInt8: return 1; + case paUInt8: return 1; + case paCustomFormat: return 1; + default: return 0; + } +} + +/* + NOTE: make sure this validation list is kept syncronised with the one in + pa_hostapi.h + + ValidateOpenStreamParameters() checks that parameters to Pa_OpenStream() + conform to the expected values as described below. This function is + also designed to be used with the proposed Pa_IsFormatSupported() function. + + There are basically two types of validation that could be performed: + Generic conformance validation, and device capability mismatch + validation. This function performs only generic conformance validation. + Validation that would require knowledge of device capabilities is + not performed because of potentially complex relationships between + combinations of parameters - for example, even if the sampleRate + seems ok, it might not be for a duplex stream - we have no way of + checking this in an API-neutral way, so we don't try. + + On success the function returns PaNoError and fills in hostApi, + hostApiInputDeviceID, and hostApiOutputDeviceID fields. On failure + the function returns an error code indicating the first encountered + parameter error. + + + If ValidateOpenStreamParameters() returns paNoError, the following + assertions are guaranteed to be true. + + - at least one of inputParameters & outputParmeters is valid (not NULL) + + - if inputParameters & outputParmeters are both valid, that + inputParameters->device & outputParmeters->device both use the same host api + + PaDeviceIndex inputParameters->device + - is within range (0 to Pa_CountDevices-1) Or: + - is paUseHostApiSpecificDeviceSpecification and + inputParameters->hostApiSpecificStreamInfo is non-NULL and refers + to a valid host api + + int inputParameters->channelCount + - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, channelCount is > 0 + - upper bound is NOT validated against device capabilities + + PaSampleFormat inputParameters->sampleFormat + - is one of the sample formats defined in portaudio.h + + void *inputParameters->hostApiSpecificStreamInfo + - if supplied its hostApi field matches the input device's host Api + + PaDeviceIndex outputParmeters->device + - is within range (0 to Pa_CountDevices-1) + + int outputParmeters->channelCount + - if inputDevice is valid, channelCount is > 0 + - upper bound is NOT validated against device capabilities + + PaSampleFormat outputParmeters->sampleFormat + - is one of the sample formats defined in portaudio.h + + void *outputParmeters->hostApiSpecificStreamInfo + - if supplied its hostApi field matches the output device's host Api + + double sampleRate + - is not an 'absurd' rate (less than 1000. or greater than 200000.) + - sampleRate is NOT validated against device capabilities + + PaStreamFlags streamFlags + - unused platform neutral flags are zero +*/ +static PaError ValidateOpenStreamParameters( + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + PaStreamFlags streamFlags, + PaUtilHostApiRepresentation **hostApi, + PaDeviceIndex *hostApiInputDevice, + PaDeviceIndex *hostApiOutputDevice ) +{ + int inputHostApiIndex=0, outputHostApiIndex=0; + + if( (inputParameters == NULL) && (outputParameters == NULL) ) + { + + return paInvalidDevice; /* @todo should be a new error code "invalid device parameters" or something */ + + } + else + { + if( inputParameters == NULL ) + { + *hostApiInputDevice = paNoDevice; + } + else if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + if( inputParameters->hostApiSpecificStreamInfo ) + { + inputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( + ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType ); + + if( inputHostApiIndex != -1 ) + { + *hostApiInputDevice = paUseHostApiSpecificDeviceSpecification; + *hostApi = hostApis_[inputHostApiIndex]; + } + else + { + return paInvalidDevice; + } + } + else + { + return paInvalidDevice; + } + } + else + { + if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ ) + return paInvalidDevice; + + inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice ); + if( inputHostApiIndex < 0 ) + return paInternalError; + + *hostApi = hostApis_[inputHostApiIndex]; + + if( inputParameters->channelCount <= 0 ) + return paInvalidChannelCount; + + if( !SampleFormatIsValid( inputParameters->sampleFormat ) ) + return paSampleFormatNotSupported; + + if( inputParameters->hostApiSpecificStreamInfo != NULL ) + { + if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType + != (*hostApi)->info.type ) + return paIncompatibleHostApiSpecificStreamInfo; + } + } + + if( outputParameters == NULL ) + { + *hostApiOutputDevice = paNoDevice; + } + else if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + { + if( outputParameters->hostApiSpecificStreamInfo ) + { + outputHostApiIndex = Pa_HostApiTypeIdToHostApiIndex( + ((PaUtilHostApiSpecificStreamInfoHeader*)outputParameters->hostApiSpecificStreamInfo)->hostApiType ); + + if( outputHostApiIndex != -1 ) + { + *hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification; + *hostApi = hostApis_[outputHostApiIndex]; + } + else + { + return paInvalidDevice; + } + } + else + { + return paInvalidDevice; + } + } + else + { + if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ ) + return paInvalidDevice; + + outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice ); + if( outputHostApiIndex < 0 ) + return paInternalError; + + *hostApi = hostApis_[outputHostApiIndex]; + + if( outputParameters->channelCount <= 0 ) + return paInvalidChannelCount; + + if( !SampleFormatIsValid( outputParameters->sampleFormat ) ) + return paSampleFormatNotSupported; + + if( outputParameters->hostApiSpecificStreamInfo != NULL ) + { + if( ((PaUtilHostApiSpecificStreamInfoHeader*)inputParameters->hostApiSpecificStreamInfo)->hostApiType + != (*hostApi)->info.type ) + return paIncompatibleHostApiSpecificStreamInfo; + } + } + + if( (inputParameters != NULL) && (outputParameters != NULL) ) + { + /* ensure that both devices use the same API */ + if( inputHostApiIndex != outputHostApiIndex ) + return paBadIODeviceCombination; + } + } + + + /* Check for absurd sample rates. */ + if( (sampleRate < 1000.0) || (sampleRate > 200000.0) ) + return paInvalidSampleRate; + + if( ((streamFlags & ~paPlatformSpecificFlags) & ~(paClipOff | paDitherOff | paNeverDropInput | paPrimeOutputBuffersUsingStreamCallback ) ) != 0 ) + return paInvalidFlag; + + return paNoError; +} + + +PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + PaError result; + PaUtilHostApiRepresentation *hostApi; + PaDeviceIndex hostApiInputDevice, hostApiOutputDevice; + PaStreamParameters hostApiInputParameters, hostApiOutputParameters; + PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; + + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsFormatSupported called:\n" ); + + if( inputParameters == NULL ){ + PaUtil_DebugPrint("\PaStreamParameters *inputParameters: NULL\n" ); + }else{ + PaUtil_DebugPrint("\PaStreamParameters *inputParameters: 0x%p\n", inputParameters ); + PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device ); + PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount ); + PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat ); + PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency ); + PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo ); + } + + if( outputParameters == NULL ){ + PaUtil_DebugPrint("\PaStreamParameters *outputParameters: NULL\n" ); + }else{ + PaUtil_DebugPrint("\PaStreamParameters *outputParameters: 0x%p\n", outputParameters ); + PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device ); + PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount ); + PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat ); + PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency ); + PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo ); + } + + PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + result = ValidateOpenStreamParameters( inputParameters, + outputParameters, + sampleRate, paNoFlag, + &hostApi, + &hostApiInputDevice, + &hostApiOutputDevice ); + if( result != paNoError ) + { +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsFormatSupported returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + + if( inputParameters ) + { + hostApiInputParameters.device = hostApiInputDevice; + hostApiInputParameters.channelCount = inputParameters->channelCount; + hostApiInputParameters.sampleFormat = inputParameters->sampleFormat; + hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency; + hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo; + hostApiInputParametersPtr = &hostApiInputParameters; + } + else + { + hostApiInputParametersPtr = NULL; + } + + if( outputParameters ) + { + hostApiOutputParameters.device = hostApiOutputDevice; + hostApiOutputParameters.channelCount = outputParameters->channelCount; + hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat; + hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency; + hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo; + hostApiOutputParametersPtr = &hostApiOutputParameters; + } + else + { + hostApiOutputParametersPtr = NULL; + } + + result = hostApi->IsFormatSupported( hostApi, + hostApiInputParametersPtr, hostApiOutputParametersPtr, + sampleRate ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + if( result == paFormatIsSupported ) + PaUtil_DebugPrint("\tPaError: 0 [ paFormatIsSupported ]\n\n" ); + else + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_OpenStream( PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result; + PaUtilHostApiRepresentation *hostApi; + PaDeviceIndex hostApiInputDevice, hostApiOutputDevice; + PaStreamParameters hostApiInputParameters, hostApiOutputParameters; + PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; + + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream called:\n" ); + PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream ); + + if( inputParameters == NULL ){ + PaUtil_DebugPrint("\PaStreamParameters *inputParameters: NULL\n" ); + }else{ + PaUtil_DebugPrint("\PaStreamParameters *inputParameters: 0x%p\n", inputParameters ); + PaUtil_DebugPrint("\tPaDeviceIndex inputParameters->device: %d\n", inputParameters->device ); + PaUtil_DebugPrint("\tint inputParameters->channelCount: %d\n", inputParameters->channelCount ); + PaUtil_DebugPrint("\tPaSampleFormat inputParameters->sampleFormat: %d\n", inputParameters->sampleFormat ); + PaUtil_DebugPrint("\tPaTime inputParameters->suggestedLatency: %f\n", inputParameters->suggestedLatency ); + PaUtil_DebugPrint("\tvoid *inputParameters->hostApiSpecificStreamInfo: 0x%p\n", inputParameters->hostApiSpecificStreamInfo ); + } + + if( outputParameters == NULL ){ + PaUtil_DebugPrint("\PaStreamParameters *outputParameters: NULL\n" ); + }else{ + PaUtil_DebugPrint("\PaStreamParameters *outputParameters: 0x%p\n", outputParameters ); + PaUtil_DebugPrint("\tPaDeviceIndex outputParameters->device: %d\n", outputParameters->device ); + PaUtil_DebugPrint("\tint outputParameters->channelCount: %d\n", outputParameters->channelCount ); + PaUtil_DebugPrint("\tPaSampleFormat outputParameters->sampleFormat: %d\n", outputParameters->sampleFormat ); + PaUtil_DebugPrint("\tPaTime outputParameters->suggestedLatency: %f\n", outputParameters->suggestedLatency ); + PaUtil_DebugPrint("\tvoid *outputParameters->hostApiSpecificStreamInfo: 0x%p\n", outputParameters->hostApiSpecificStreamInfo ); + } + + PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); + PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer ); + PaUtil_DebugPrint("\tPaStreamFlags streamFlags: 0x%x\n", streamFlags ); + PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback ); + PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData ); +#endif + + if( !PA_IS_INITIALISED_ ) + { + result = paNotInitialized; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + /* Check for parameter errors. */ + + if( stream == NULL ) + { + result = paBadStreamPtr; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + result = ValidateOpenStreamParameters( inputParameters, + outputParameters, + sampleRate, streamFlags, + &hostApi, + &hostApiInputDevice, + &hostApiOutputDevice ); + if( result != paNoError ) + { +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): undefined\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + return result; + } + + + if( inputParameters ) + { + hostApiInputParameters.device = hostApiInputDevice; + hostApiInputParameters.channelCount = inputParameters->channelCount; + hostApiInputParameters.sampleFormat = inputParameters->sampleFormat; + hostApiInputParameters.suggestedLatency = inputParameters->suggestedLatency; + hostApiInputParameters.hostApiSpecificStreamInfo = inputParameters->hostApiSpecificStreamInfo; + hostApiInputParametersPtr = &hostApiInputParameters; + } + else + { + hostApiInputParametersPtr = NULL; + } + + if( outputParameters ) + { + hostApiOutputParameters.device = hostApiOutputDevice; + hostApiOutputParameters.channelCount = outputParameters->channelCount; + hostApiOutputParameters.sampleFormat = outputParameters->sampleFormat; + hostApiOutputParameters.suggestedLatency = outputParameters->suggestedLatency; + hostApiOutputParameters.hostApiSpecificStreamInfo = outputParameters->hostApiSpecificStreamInfo; + hostApiOutputParametersPtr = &hostApiOutputParameters; + } + else + { + hostApiOutputParametersPtr = NULL; + } + + result = hostApi->OpenStream( hostApi, stream, + hostApiInputParametersPtr, hostApiOutputParametersPtr, + sampleRate, framesPerBuffer, streamFlags, streamCallback, userData ); + + if( result == paNoError ) + AddOpenStream( *stream ); + + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p\n", *stream ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_OpenDefaultStream( PaStream** stream, + int inputChannelCount, + int outputChannelCount, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result; + PaStreamParameters hostApiInputParameters, hostApiOutputParameters; + PaStreamParameters *hostApiInputParametersPtr, *hostApiOutputParametersPtr; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenDefaultStream called:\n" ); + PaUtil_DebugPrint("\tPaStream** stream: 0x%p\n", stream ); + PaUtil_DebugPrint("\tint inputChannelCount: %d\n", inputChannelCount ); + PaUtil_DebugPrint("\tint outputChannelCount: %d\n", outputChannelCount ); + PaUtil_DebugPrint("\tPaSampleFormat sampleFormat: %d\n", sampleFormat ); + PaUtil_DebugPrint("\tdouble sampleRate: %g\n", sampleRate ); + PaUtil_DebugPrint("\tunsigned long framesPerBuffer: %d\n", framesPerBuffer ); + PaUtil_DebugPrint("\tPaStreamCallback *streamCallback: 0x%p\n", streamCallback ); + PaUtil_DebugPrint("\tvoid *userData: 0x%p\n", userData ); +#endif + + + if( inputChannelCount > 0 ) + { + hostApiInputParameters.device = Pa_GetDefaultInputDevice(); + hostApiInputParameters.channelCount = inputChannelCount; + hostApiInputParameters.sampleFormat = sampleFormat; + hostApiInputParameters.suggestedLatency = /* REVIEW: should we be using high input latency here? */ + Pa_GetDeviceInfo( hostApiInputParameters.device )->defaultHighInputLatency; + hostApiInputParameters.hostApiSpecificStreamInfo = NULL; + hostApiInputParametersPtr = &hostApiInputParameters; + } + else + { + hostApiInputParametersPtr = NULL; + } + + if( outputChannelCount > 0 ) + { + hostApiOutputParameters.device = Pa_GetDefaultOutputDevice(); + hostApiOutputParameters.channelCount = outputChannelCount; + hostApiOutputParameters.sampleFormat = sampleFormat; + hostApiOutputParameters.suggestedLatency = /* REVIEW: should we be using high input latency here? */ + Pa_GetDeviceInfo( hostApiOutputParameters.device )->defaultHighOutputLatency; + hostApiOutputParameters.hostApiSpecificStreamInfo = NULL; + hostApiOutputParametersPtr = &hostApiOutputParameters; + } + else + { + hostApiOutputParametersPtr = NULL; + } + + + result = Pa_OpenStream( + stream, hostApiInputParametersPtr, hostApiOutputParametersPtr, + sampleRate, framesPerBuffer, paNoFlag, streamCallback, userData ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_OpenDefaultStream returned:\n" ); + PaUtil_DebugPrint("\t*(PaStream** stream): 0x%p", *stream ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +static PaError ValidateStream( PaStream* stream ) +{ + if( !PA_IS_INITIALISED_ ) return paNotInitialized; + + if( stream == NULL ) return paBadStreamPtr; + + if( ((PaUtilStreamRepresentation*)stream)->magic != PA_STREAM_MAGIC ) + return paBadStreamPtr; + + return paNoError; +} + + +PaError Pa_CloseStream( PaStream* stream ) +{ + PaUtilStreamInterface *interface; + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CloseStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + /* always remove the open stream from our list, even if this function + eventually returns an error. Otherwise CloseOpenStreams() will + get stuck in an infinite loop */ + RemoveOpenStream( stream ); /* be sure to call this _before_ closing the stream */ + + if( result == paNoError ) + { + interface = PA_STREAM_INTERFACE(stream); + if( !interface->IsStopped( stream ) ) + { + result = interface->Abort( stream ); + } + + if( result == paNoError ) /* REVIEW: shouldn't we close anyway? */ + result = interface->Close( stream ); + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_CloseStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ) +{ + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_SetStreamFinishedCallback called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); + PaUtil_DebugPrint("\tPaStreamFinishedCallback* streamFinishedCallback: 0x%p\n", streamFinishedCallback ); +#endif + + if( result == paNoError ) + { + if( !PA_STREAM_INTERFACE(stream)->IsStopped( stream ) ) + { + result = paStreamIsNotStopped ; + } + else + { + PA_STREAM_REP( stream )->streamFinishedCallback = streamFinishedCallback; + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_SetStreamFinishedCallback returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; + +} + + +PaError Pa_StartStream( PaStream *stream ) +{ + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_StartStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + { + if( !PA_STREAM_INTERFACE(stream)->IsStopped( stream ) ) + { + result = paStreamIsNotStopped ; + } + else + { + result = PA_STREAM_INTERFACE(stream)->Start( stream ); + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_StartStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_StopStream( PaStream *stream ) +{ + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_StopStream called\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + { + if( PA_STREAM_INTERFACE(stream)->IsStopped( stream ) ) + { + result = paStreamIsStopped; + } + else + { + result = PA_STREAM_INTERFACE(stream)->Stop( stream ); + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_StopStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_AbortStream( PaStream *stream ) +{ + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_AbortStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + { + if( PA_STREAM_INTERFACE(stream)->IsStopped( stream ) ) + { + result = paStreamIsStopped; + } + else + { + result = PA_STREAM_INTERFACE(stream)->Abort( stream ); + } + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_AbortStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_IsStreamStopped( PaStream *stream ) +{ + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsStreamStopped called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + result = PA_STREAM_INTERFACE(stream)->IsStopped( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsStreamStopped returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_IsStreamActive( PaStream *stream ) +{ + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsStreamActive called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( result == paNoError ) + result = PA_STREAM_INTERFACE(stream)->IsActive( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_IsStreamActive returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream ) +{ + PaError error = ValidateStream( stream ); + const PaStreamInfo *result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamInfo called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" ); + PaUtil_DebugPrint("\const PaStreamInfo*: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = &PA_STREAM_REP( stream )->streamInfo; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamInfo returned:\n" ); + PaUtil_DebugPrint("\tconst PaStreamInfo*: 0x%p:\n", result ); + PaUtil_DebugPrint("\t{" ); + + PaUtil_DebugPrint("\t\tint structVersion: %d\n", result->structVersion ); + PaUtil_DebugPrint("\t\tPaTime inputLatency: %f\n", result->inputLatency ); + PaUtil_DebugPrint("\t\tPaTime outputLatency: %f\n", result->outputLatency ); + PaUtil_DebugPrint("\t\tdouble sampleRate: %f\n", result->sampleRate ); + PaUtil_DebugPrint("\t}\n\n" ); +#endif + + } + + return result; +} + + +PaTime Pa_GetStreamTime( PaStream *stream ) +{ + PaError error = ValidateStream( stream ); + PaTime result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamTime called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" ); + PaUtil_DebugPrint("\tPaTime: 0 [PaError error:%d ( %s )]\n\n", result, error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = PA_STREAM_INTERFACE(stream)->GetTime( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamTime returned:\n" ); + PaUtil_DebugPrint("\tPaTime: %g\n\n", result ); +#endif + + } + + return result; +} + + +double Pa_GetStreamCpuLoad( PaStream* stream ) +{ + PaError error = ValidateStream( stream ); + double result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamCpuLoad called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + + result = 0.0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" ); + PaUtil_DebugPrint("\tdouble: 0.0 [PaError error: %d ( %s )]\n\n", error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = PA_STREAM_INTERFACE(stream)->GetCpuLoad( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamCpuLoad returned:\n" ); + PaUtil_DebugPrint("\tdouble: %g\n\n", result ); +#endif + + } + + return result; +} + + +PaError Pa_ReadStream( PaStream* stream, + void *buffer, + unsigned long frames ) +{ + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_ReadStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + /* @todo should return an error if buffer is zero or frames <= 0 */ + if( frames > 0 && buffer != 0 ) + result = PA_STREAM_INTERFACE(stream)->Read( stream, buffer, frames ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_ReadStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + + +PaError Pa_WriteStream( PaStream* stream, + void *buffer, + unsigned long frames ) +{ + PaError result = ValidateStream( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_WriteStream called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + /* @todo should return an error if buffer is zero or frames <= 0 */ + if( frames > 0 && buffer != 0 ) + result = PA_STREAM_INTERFACE(stream)->Write( stream, buffer, frames ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_WriteStream returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return result; +} + +signed long Pa_GetStreamReadAvailable( PaStream* stream ) +{ + PaError error = ValidateStream( stream ); + signed long result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamReadAvailable called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" ); + PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = PA_STREAM_INTERFACE(stream)->GetReadAvailable( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamReadAvailable returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + } + + return result; +} + + +signed long Pa_GetStreamWriteAvailable( PaStream* stream ) +{ + PaError error = ValidateStream( stream ); + signed long result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamWriteAvailable called:\n" ); + PaUtil_DebugPrint("\tPaStream* stream: 0x%p\n", stream ); +#endif + + if( error != paNoError ) + { + result = 0; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" ); + PaUtil_DebugPrint("\tunsigned long: 0 [ PaError error: %d ( %s ) ]\n\n", error, Pa_GetErrorText( error ) ); +#endif + + } + else + { + result = PA_STREAM_INTERFACE(stream)->GetWriteAvailable( stream ); + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetStreamWriteAvailable returned:\n" ); + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + } + + return result; +} + + +PaError Pa_GetSampleSize( PaSampleFormat format ) +{ + int result; + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetSampleSize called:\n" ); + PaUtil_DebugPrint("\tPaSampleFormat format: %d\n", format ); +#endif + + switch( format & ~paNonInterleaved ) + { + + case paUInt8: + case paInt8: + result = 1; + break; + + case paInt16: + result = 2; + break; + + case paInt24: + result = 3; + break; + + case paFloat32: + case paInt32: + result = 4; + break; + + default: + result = paSampleFormatNotSupported; + break; + } + +#ifdef PA_LOG_API_CALLS + PaUtil_DebugPrint("Pa_GetSampleSize returned:\n" ); + if( result > 0 ) + PaUtil_DebugPrint("\tint: %d\n\n", result ); + else + PaUtil_DebugPrint("\tPaError: %d ( %s )\n\n", result, Pa_GetErrorText( result ) ); +#endif + + return (PaError) result; +} + diff --git a/pd/portaudio/pa_common/pa_front.o b/pd/portaudio/pa_common/pa_front.o new file mode 100644 index 00000000..0ce74a68 Binary files /dev/null and b/pd/portaudio/pa_common/pa_front.o differ diff --git a/pd/portaudio/pa_common/pa_hostapi.h b/pd/portaudio/pa_common/pa_hostapi.h new file mode 100644 index 00000000..f1f7a7ea --- /dev/null +++ b/pd/portaudio/pa_common/pa_hostapi.h @@ -0,0 +1,238 @@ +#ifndef PA_HOSTAPI_H +#define PA_HOSTAPI_H +/* + * + * Portable Audio I/O Library + * host api representation + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** @file + Virtualised host api mechanism used by pa_front. +*/ + +#include "portaudio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +typedef struct PaUtilPrivatePaFrontHostApiInfo { +/* **for the use of pa_front.c only** + don't use fields in this structure, they my change at any time + use functions defined in pa_util.h if you think you need functionality + which can be derived from here +*/ + + unsigned long baseDeviceIndex; +}PaUtilPrivatePaFrontHostApiInfo; + + +/** The common header for all data structures whose pointers are passed through + the hostApiSpecificStreamInfo field of the PaStreamParameters structure. + Note that in order to keep the public PortAudio interface clean, this structure + is not used explicitly when declaring hostApiSpecificStreamInfo data structures + however some code in pa_front depends on the first 3 members being equivalent + with this structure. + @see PaStreamParameters +*/ +typedef struct PaUtilHostApiSpecificStreamInfoHeader +{ + unsigned long size; /**< size of whole structure including this header */ + PaHostApiTypeId hostApiType; /**< host API for which this data is intended */ + unsigned long version; /**< structure version */ +} PaUtilHostApiSpecificStreamInfoHeader; + + + +/* + PaUtilHostApiRepresentation must be implemented by each host api implementation. + +*/ + +typedef struct PaUtilHostApiRepresentation { + PaUtilPrivatePaFrontHostApiInfo privatePaFrontInfo; + + /** + The host api implementation should populate the info field. In the + case of info.defaultInputDevice and info.defaultOutputDevice the + values stored should be 0 based indicies within the host api's own + device index range (0 to deviceCount). These values will be converted + to global device indicies after PaUtilHostApiInitializer() returns. + */ + PaHostApiInfo info; + + PaDeviceInfo** deviceInfos; + + /** + (*Terminate)() is guaranteed to be called with a valid + parameter, which was previously returned from the same implementation's + initializer. + */ + void (*Terminate)( struct PaUtilHostApiRepresentation *hostApi ); + + /** + The inputParameters and outputParameters pointers should not be saved + as they will not remain valid after OpenStream is called. + + + The following guarantees are made about parameters to (*OpenStream)(): + + [NOTE: the following list up to *END PA FRONT VALIDATIONS* should be + kept in sync with the one for ValidateOpenStreamParameters and + Pa_OpenStream in pa_front.c] + + PaHostApiRepresentation *hostApi + - is valid for this implementation + + PaStream** stream + - is non-null + + - at least one of inputParameters & outputParmeters is valid (not NULL) + + - if inputParameters & outputParmeters are both valid, that + inputParameters->device & outputParmeters->device both use the same host api + + PaDeviceIndex inputParameters->device + - is within range (0 to Pa_CountDevices-1) Or: + - is paUseHostApiSpecificDeviceSpecification and + inputParameters->hostApiSpecificStreamInfo is non-NULL and refers + to a valid host api + + int inputParameters->numChannels + - if inputParameters->device is not paUseHostApiSpecificDeviceSpecification, numInputChannels is > 0 + - upper bound is NOT validated against device capabilities + + PaSampleFormat inputParameters->sampleFormat + - is one of the sample formats defined in portaudio.h + + void *inputParameters->hostApiSpecificStreamInfo + - if supplied its hostApi field matches the input device's host Api + + PaDeviceIndex outputParmeters->device + - is within range (0 to Pa_CountDevices-1) + + int outputParmeters->numChannels + - if inputDevice is valid, numInputChannels is > 0 + - upper bound is NOT validated against device capabilities + + PaSampleFormat outputParmeters->sampleFormat + - is one of the sample formats defined in portaudio.h + + void *outputParmeters->hostApiSpecificStreamInfo + - if supplied its hostApi field matches the output device's host Api + + double sampleRate + - is not an 'absurd' rate (less than 1000. or greater than 200000.) + - sampleRate is NOT validated against device capabilities + + PaStreamFlags streamFlags + - unused platform neutral flags are zero + + [*END PA FRONT VALIDATIONS*] + + + The following validations MUST be performed by (*OpenStream)(): + + - check that input device can support numInputChannels + + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if inputStreamInfo is supplied, validate its contents, + or return an error if no inputStreamInfo is expected + + - check that output device can support numOutputChannels + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if outputStreamInfo is supplied, validate its contents, + or return an error if no outputStreamInfo is expected + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + + - alter sampleRate to a close allowable rate if necessary + + - validate inputLatency and outputLatency + + - validate any platform specific flags, if flags are supplied they + must be valid. + */ + PaError (*OpenStream)( struct PaUtilHostApiRepresentation *hostApi, + PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerCallback, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); + + + PaError (*IsFormatSupported)( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +} PaUtilHostApiRepresentation; + + +/** + every host api implementation must supply a host api initializer in the + following form. +*/ +typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostApiIndex ); + + +/** + paHostApiInitializers is a NULL-terminated array of host api initializers + for the host apis which will be initialized when Pa_Initialize() is called. + each platform has a file which defines paHostApiInitializers for that platform. + see pa_win_init.c for example. +*/ +extern PaUtilHostApiInitializer *paHostApiInitializers[]; + + +/** index of the default host API in the paHostApiInitializers array. Each + platform has a file which defines paDefaultHostApiIndex for that platform. + see pa_win_init.c for example. +*/ +extern int paDefaultHostApiIndex; + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_HOSTAPI_H */ diff --git a/pd/portaudio/pa_common/pa_process.c b/pd/portaudio/pa_common/pa_process.c new file mode 100644 index 00000000..2fbeb277 --- /dev/null +++ b/pd/portaudio/pa_common/pa_process.c @@ -0,0 +1,1355 @@ +/* + * $Id: pa_process.c,v 1.1.2.28 2002/10/26 05:33:29 rossbencina Exp $ + * Portable Audio I/O Library + * streamCallback <-> host buffer processing adapter + * + * 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. + */ + +#include +#include /* memset() */ + +#include "pa_process.h" +#include "pa_util.h" + +/** @file + The code in this file is not optimised yet. there may appear to be redundancies + that could be factored into common functions, but the redundanceis are left + intentionally as each appearance may have different optimisation possibilities. + + The optimisations which are planned involve only converting data in-place + where possible, rather than copying to the temp buffer(s). + + Note that in the extreme case of being able to convert in-place, and there + being no conversion necessary there should be some code which short-circuits + the operation. + + Cache tilings for intereave<->deinterleave also need to be considered. + + @todo The abort flag from the streamCallback is currently not honoured properly + in this file, see fixmes. + + @todo see FIXMEs + + @todo implement the streamFlags callback parameter, currently it is + always zero. It needs to be passed from the host layer somehow. +*/ + +#define PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_ 1024 + + +/* greatest common divisor - PGCD in french */ +static unsigned long GCD( unsigned long a, unsigned long b ) +{ + return (b==0) ? a : GCD( b, a%b); +} + +/* least common multiple - PPCM in french */ +static unsigned long LCM( unsigned long a, unsigned long b ) +{ + return (a*b) / GCD(a,b); +} + +#define PA_MAX_( a, b ) (((a) > (b)) ? (a) : (b)) + +static unsigned long CalculateFrameShift( unsigned long M, unsigned long N ) +{ + unsigned long result = 0; + unsigned long i; + unsigned long lcm; + lcm = LCM( M, N ); + for( i = M; i < lcm; i += M ) + result = PA_MAX_( result, i % N ); + + return result; +} + + +PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bp, + int inputChannelCount, PaSampleFormat userInputSampleFormat, + PaSampleFormat hostInputSampleFormat, + int outputChannelCount, PaSampleFormat userOutputSampleFormat, + PaSampleFormat hostOutputSampleFormat, + double sampleRate, + PaStreamFlags streamFlags, + unsigned long framesPerUserBuffer, + unsigned long framesPerHostBuffer, + PaUtilHostBufferSizeMode hostBufferSizeMode, + PaStreamCallback *streamCallback, void *userData ) +{ + PaError result = paNoError; + PaError bytesPerSample; + unsigned long tempInputBufferSize, tempOutputBufferSize; + + /* initialize buffer ptrs to zero so they can be freed if necessary in error */ + bp->tempInputBuffer = 0; + bp->tempInputBufferPtrs = 0; + bp->tempOutputBuffer = 0; + bp->tempOutputBufferPtrs = 0; + + bp->framesPerUserBuffer = framesPerUserBuffer; + bp->framesPerHostBuffer = framesPerHostBuffer; + + bp->inputChannelCount = inputChannelCount; + bp->outputChannelCount = outputChannelCount; + + bp->hostBufferSizeMode = hostBufferSizeMode; + + bp->hostInputChannels[0] = 0; + bp->hostOutputChannels[0] = 0; + + if( framesPerUserBuffer == 0 ) /* streamCallback will accept any buffer size */ + { + bp->useNonAdaptingProcess = 1; + bp->framesInTempInputBuffer = 0; + bp->framesInTempOutputBuffer = 0; + + if( hostBufferSizeMode == paUtilFixedHostBufferSize + || hostBufferSizeMode == paUtilBoundedHostBufferSize ) + { + bp->framesPerTempBuffer = framesPerHostBuffer; + } + else /* unknown host buffer size */ + { + bp->framesPerTempBuffer = PA_FRAMES_PER_TEMP_BUFFER_WHEN_HOST_BUFFER_SIZE_IS_UNKNOWN_; + } + } + else + { + bp->framesPerTempBuffer = framesPerUserBuffer; + + if( hostBufferSizeMode == paUtilFixedHostBufferSize + && framesPerHostBuffer % framesPerUserBuffer == 0 ) + { + bp->useNonAdaptingProcess = 1; + bp->framesInTempInputBuffer = 0; + bp->framesInTempOutputBuffer = 0; + } + else + { + bp->useNonAdaptingProcess = 0; + + if( inputChannelCount > 0 && outputChannelCount > 0 ) + { + /* full duplex */ + if( hostBufferSizeMode == paUtilFixedHostBufferSize ) + { + unsigned long frameShift = + CalculateFrameShift( framesPerHostBuffer, framesPerUserBuffer ); + + if( framesPerUserBuffer > framesPerHostBuffer ) + { + bp->framesInTempInputBuffer = frameShift; + bp->framesInTempOutputBuffer = 0; + } + else + { + bp->framesInTempInputBuffer = 0; + bp->framesInTempOutputBuffer = frameShift; + } + } + else /* variable host buffer size, add framesPerUserBuffer latency */ + { + bp->framesInTempInputBuffer = 0; + bp->framesInTempOutputBuffer = framesPerUserBuffer; + } + } + else + { + /* half duplex */ + bp->framesInTempInputBuffer = 0; + bp->framesInTempOutputBuffer = 0; + } + } + } + + + + if( inputChannelCount > 0 ) + { + bytesPerSample = Pa_GetSampleSize( hostInputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerHostInputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bytesPerSample = Pa_GetSampleSize( userInputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerUserInputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bp->inputConverter = + PaUtil_SelectConverter( hostInputSampleFormat, userInputSampleFormat, streamFlags ); + + + bp->userInputIsInterleaved = (userInputSampleFormat & paNonInterleaved)?0:1; + + + tempInputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserInputSample * inputChannelCount; + + bp->tempInputBuffer = PaUtil_AllocateMemory( tempInputBufferSize ); + if( bp->tempInputBuffer == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + if( bp->framesInTempInputBuffer > 0 ) + memset( bp->tempInputBuffer, 0, tempInputBufferSize ); + + if( userInputSampleFormat & paNonInterleaved ) + { + bp->tempInputBufferPtrs = + PaUtil_AllocateMemory( sizeof(void*)*inputChannelCount ); + if( bp->tempInputBufferPtrs == 0 ) + { + result = paInsufficientMemory; + goto error; + } + } + + bp->hostInputChannels[0] = (PaUtilChannelDescriptor*) + PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor) * inputChannelCount * 2); + if( bp->hostInputChannels[0] == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + bp->hostInputChannels[1] = &bp->hostInputChannels[0][inputChannelCount]; + } + + if( outputChannelCount > 0 ) + { + bytesPerSample = Pa_GetSampleSize( hostOutputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerHostOutputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bytesPerSample = Pa_GetSampleSize( userOutputSampleFormat ); + if( bytesPerSample > 0 ) + { + bp->bytesPerUserOutputSample = bytesPerSample; + } + else + { + result = bytesPerSample; + goto error; + } + + bp->outputConverter = + PaUtil_SelectConverter( userOutputSampleFormat, hostOutputSampleFormat, streamFlags ); + + + bp->userOutputIsInterleaved = (userOutputSampleFormat & paNonInterleaved)?0:1; + + tempOutputBufferSize = + bp->framesPerTempBuffer * bp->bytesPerUserOutputSample * outputChannelCount; + + bp->tempOutputBuffer = PaUtil_AllocateMemory( tempOutputBufferSize ); + if( bp->tempOutputBuffer == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + if( bp->framesInTempOutputBuffer > 0 ) + memset( bp->tempOutputBuffer, 0, tempOutputBufferSize ); + + if( userOutputSampleFormat & paNonInterleaved ) + { + bp->tempOutputBufferPtrs = + PaUtil_AllocateMemory( sizeof(void*)*outputChannelCount ); + if( bp->tempOutputBufferPtrs == 0 ) + { + result = paInsufficientMemory; + goto error; + } + } + + bp->hostOutputChannels[0] = (PaUtilChannelDescriptor*) + PaUtil_AllocateMemory( sizeof(PaUtilChannelDescriptor)*outputChannelCount * 2 ); + if( bp->hostOutputChannels[0] == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + bp->hostOutputChannels[1] = &bp->hostOutputChannels[0][outputChannelCount]; + } + + PaUtil_InitializeTriangularDitherState( &bp->ditherGenerator ); + + bp->samplePeriod = 1. / sampleRate; + + bp->streamCallback = streamCallback; + bp->userData = userData; + + return result; + +error: + if( bp->tempInputBuffer ) + PaUtil_FreeMemory( bp->tempInputBuffer ); + + if( bp->tempInputBufferPtrs ) + PaUtil_FreeMemory( bp->tempInputBufferPtrs ); + + if( bp->hostInputChannels[0] ) + PaUtil_FreeMemory( bp->hostInputChannels[0] ); + + if( bp->tempOutputBuffer ) + PaUtil_FreeMemory( bp->tempOutputBuffer ); + + if( bp->tempOutputBufferPtrs ) + PaUtil_FreeMemory( bp->tempOutputBufferPtrs ); + + if( bp->hostOutputChannels[0] ) + PaUtil_FreeMemory( bp->hostOutputChannels[0] ); + + return result; +} + + +void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bp ) +{ + if( bp->tempInputBuffer ) + PaUtil_FreeMemory( bp->tempInputBuffer ); + + if( bp->tempInputBufferPtrs ) + PaUtil_FreeMemory( bp->tempInputBufferPtrs ); + + if( bp->hostInputChannels[0] ) + PaUtil_FreeMemory( bp->hostInputChannels[0] ); + + if( bp->tempOutputBuffer ) + PaUtil_FreeMemory( bp->tempOutputBuffer ); + + if( bp->tempOutputBufferPtrs ) + PaUtil_FreeMemory( bp->tempOutputBufferPtrs ); + + if( bp->hostOutputChannels[0] ) + PaUtil_FreeMemory( bp->hostOutputChannels[0] ); +} + + +void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bp, PaStreamCallbackTimeInfo* timeInfo ) +{ + bp->timeInfo = timeInfo; + + /* the first streamCallback will be called to process samples which are + currently in the input buffer before the ones starting at the timeInfo time */ + + bp->timeInfo->inputBufferAdcTime -= bp->framesInTempInputBuffer * bp->samplePeriod; + + (void)bp->timeInfo->currentTime; /* FIXME: @todo time info currentTime not implemented */ + + /* the first streamCallback will be called to generate samples which will be + outputted after the frames currently in the output buffer have been + outputted. */ + bp->timeInfo->outputBufferDacTime += bp->framesInTempOutputBuffer * bp->samplePeriod; + + bp->hostInputFrameCount[1] = 0; + bp->hostOutputFrameCount[1] = 0; +} + + +static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostInputChannels, + PaUtilChannelDescriptor *hostOutputChannels, + unsigned long frameCount ); + +static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostInputChannels, + unsigned long frameCount ); + +static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostOutputChannels, + unsigned long framesToProcess ); + +static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, int processPartialUserBuffers ); + +#define PA_MIN_( a, b ) ( ((a)<(b)) ? (a) : (b) ) + +unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bp, int *streamCallbackResult ) +{ + unsigned long framesToProcess, framesToGo; + unsigned long framesProcessed = 0; + + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) + { + assert( (bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) == + (bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) ); + } + + + if( bp->useNonAdaptingProcess ) + { + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) + { + /* full duplex non-adapting process, splice buffers if they are + different lengths */ + + framesToGo = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]; /* relies on assert above for input/output equivalence */ + + do{ + unsigned long *hostInputFrameCount; + PaUtilChannelDescriptor *hostInputChannels; + unsigned long *hostOutputFrameCount; + PaUtilChannelDescriptor *hostOutputChannels; + unsigned long framesProcessedThisIteration; + + if( bp->hostInputFrameCount[0] != 0 ) + { + hostInputFrameCount = &bp->hostInputFrameCount[0]; + hostInputChannels = bp->hostInputChannels[0]; + } + else + { + hostInputFrameCount = &bp->hostInputFrameCount[1]; + hostInputChannels = bp->hostInputChannels[1]; + } + + if( bp->hostOutputFrameCount[0] != 0 ) + { + hostOutputFrameCount = &bp->hostOutputFrameCount[0]; + hostOutputChannels = bp->hostOutputChannels[0]; + } + else + { + hostOutputFrameCount = &bp->hostOutputFrameCount[1]; + hostOutputChannels = bp->hostOutputChannels[1]; + } + + framesToProcess = PA_MIN_( *hostInputFrameCount, + *hostOutputFrameCount ); + + assert( framesToProcess != 0 ); + + framesProcessedThisIteration = NonAdaptingProcess( bp, streamCallbackResult, + hostInputChannels, hostOutputChannels, + framesToProcess ); + + *hostInputFrameCount -= framesProcessedThisIteration; + *hostOutputFrameCount -= framesProcessedThisIteration; + + framesProcessed += framesProcessedThisIteration; + framesToGo -= framesProcessedThisIteration; + + }while( framesToGo > 0 ); + } + else + { + /* half duplex non-adapting process, just process 1st and 2nd buffer */ + /* process first buffer */ + + framesToProcess = (bp->inputChannelCount != 0) + ? bp->hostInputFrameCount[0] + : bp->hostOutputFrameCount[0]; + + framesProcessed = NonAdaptingProcess( bp, streamCallbackResult, + bp->hostInputChannels[0], bp->hostOutputChannels[0], + framesToProcess ); + + /* process second buffer if provided */ + + framesToProcess = (bp->inputChannelCount != 0) + ? bp->hostInputFrameCount[1] + : bp->hostOutputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += NonAdaptingProcess( bp, streamCallbackResult, + bp->hostInputChannels[1], bp->hostOutputChannels[1], + framesToProcess ); + } + } + } + else /* block adaption necessary*/ + { + + if( bp->inputChannelCount != 0 && bp->outputChannelCount != 0 ) + { + /* full duplex */ + + if( bp->hostBufferSizeMode == paUtilVariableHostBufferSizePartialUsageAllowed ) + { + framesProcessed = AdaptingProcess( bp, streamCallbackResult, + 0 /* dont process partial user buffers */ ); + } + else + { + framesProcessed = AdaptingProcess( bp, streamCallbackResult, + 1 /* process partial user buffers */ ); + } + } + else if( bp->inputChannelCount != 0 ) + { + /* input only */ + framesToProcess = bp->hostInputFrameCount[0]; + + framesProcessed = AdaptingInputOnlyProcess( bp, streamCallbackResult, + bp->hostInputChannels[0], framesToProcess ); + + framesToProcess = bp->hostInputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += AdaptingInputOnlyProcess( bp, streamCallbackResult, + bp->hostInputChannels[1], framesToProcess ); + } + } + else + { + /* output only */ + framesToProcess = bp->hostOutputFrameCount[0]; + + framesProcessed = AdaptingOutputOnlyProcess( bp, streamCallbackResult, + bp->hostOutputChannels[0], framesToProcess ); + + framesToProcess = bp->hostOutputFrameCount[1]; + if( framesToProcess > 0 ) + { + framesProcessed += AdaptingOutputOnlyProcess( bp, streamCallbackResult, + bp->hostOutputChannels[1], framesToProcess ); + } + } + } + + return framesProcessed; +} + + +void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + if( frameCount == 0 ) + bp->hostInputFrameCount[0] = bp->framesPerHostBuffer; + else + bp->hostInputFrameCount[0] = frameCount; +} + + +void PaUtil_SetInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[0][channel].data = data; + bp->hostInputChannels[0][channel].stride = stride; +} + + +void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->inputChannelCount; + + assert( firstChannel < bp->inputChannelCount ); + assert( firstChannel + channelCount <= bp->inputChannelCount ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostInputChannels[0][channel+i].data = p; + p += bp->bytesPerHostInputSample; + bp->hostInputChannels[0][channel+i].stride = channelCount; + } +} + + +void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[0][channel].data = data; + bp->hostInputChannels[0][channel].stride = 1; +} + + +void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + bp->hostInputFrameCount[1] = frameCount; +} + + +void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[1][channel].data = data; + bp->hostInputChannels[1][channel].stride = stride; +} + + +void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->inputChannelCount; + + assert( firstChannel < bp->inputChannelCount ); + assert( firstChannel + channelCount <= bp->inputChannelCount ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostInputChannels[1][channel+i].data = p; + p += bp->bytesPerHostInputSample; + bp->hostInputChannels[1][channel+i].stride = channelCount; + } +} + + +void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->inputChannelCount ); + + bp->hostInputChannels[1][channel].data = data; + bp->hostInputChannels[1][channel].stride = 1; +} + + +void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + if( frameCount == 0 ) + bp->hostOutputFrameCount[0] = bp->framesPerHostBuffer; + else + bp->hostOutputFrameCount[0] = frameCount; +} + + +void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->outputChannelCount ); + + bp->hostOutputChannels[0][channel].data = data; + bp->hostOutputChannels[0][channel].stride = stride; +} + + +void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->outputChannelCount; + + assert( firstChannel < bp->outputChannelCount ); + assert( firstChannel + channelCount <= bp->outputChannelCount ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostOutputChannels[0][channel+i].data = p; + p += bp->bytesPerHostOutputSample; + bp->hostOutputChannels[0][channel+i].stride = channelCount; + } +} + + +void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->outputChannelCount ); + + bp->hostOutputChannels[0][channel].data = data; + bp->hostOutputChannels[0][channel].stride = 1; +} + + +void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bp, + unsigned long frameCount ) +{ + bp->hostOutputFrameCount[1] = frameCount; +} + + +void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data, unsigned int stride ) +{ + assert( channel < bp->outputChannelCount ); + + bp->hostOutputChannels[1][channel].data = data; + bp->hostOutputChannels[1][channel].stride = stride; +} + + +void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bp, + unsigned int firstChannel, void *data, unsigned int channelCount ) +{ + unsigned int i; + unsigned int channel = firstChannel; + unsigned char *p = (unsigned char*)data; + + if( channelCount == 0 ) + channelCount = bp->outputChannelCount; + + assert( firstChannel < bp->outputChannelCount ); + assert( firstChannel + channelCount <= bp->outputChannelCount ); + + for( i=0; i< channelCount; ++i ) + { + bp->hostOutputChannels[1][channel+i].data = p; + p += bp->bytesPerHostOutputSample; + bp->hostOutputChannels[1][channel+i].stride = channelCount; + } +} + + +void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bp, + unsigned int channel, void *data ) +{ + assert( channel < bp->outputChannelCount ); + + bp->hostOutputChannels[1][channel].data = data; + bp->hostOutputChannels[1][channel].stride = 1; +} + + +/* + NonAdaptingProcess() is a simple buffer copying adaptor that can handle + both full and half duplex copies. It processes framesToProcess frames, + broken into blocks bp->framesPerTempBuffer long. + This routine can be used when the streamCallback doesn't care what length the + buffers are, or when framesToProcess is an integer multiple of + bp->framesPerTempBuffer, in which case streamCallback will always be called + with bp->framesPerTempBuffer samples. +*/ +static unsigned long NonAdaptingProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostInputChannels, + PaUtilChannelDescriptor *hostOutputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *srcBytePtr, *destBytePtr; + unsigned int srcStride, srcBytePtrStride; + unsigned int destStride, destBytePtrStride; + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + unsigned long statusFlags = 0; // FIXME: implement this + + do + { + frameCount = ( bp->framesPerTempBuffer < framesToGo ) + ? bp->framesPerTempBuffer + : framesToGo; + + /* configure user input buffer and convert input data (host -> user) */ + if( bp->inputChannelCount == 0 ) + { + /* no input */ + userInput = 0; + } + else /* there are input channels */ + { + /* + could use more elaborate logic here and sometimes process + buffers in-place. + */ + + destBytePtr = bp->tempInputBuffer; + + if( bp->userInputIsInterleaved ) + { + destStride = bp->inputChannelCount; + destBytePtrStride = bp->bytesPerUserInputSample; + userInput = bp->tempInputBuffer; + } + else /* user input is not interleaved */ + { + destStride = 1; + destBytePtrStride = frameCount * bp->bytesPerUserInputSample; + + /* setup non-interleaved ptrs */ + for( i=0; iinputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->bytesPerUserInputSample * frameCount; + } + + userInput = bp->tempInputBufferPtrs; + } + + for( i=0; iinputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destStride, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destBytePtrStride; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + } + + /* configure user output buffer */ + if( bp->outputChannelCount == 0 ) + { + /* no output */ + userOutput = 0; + } + else /* there are output channels */ + { + if( bp->userOutputIsInterleaved ) + { + userOutput = bp->tempOutputBuffer; + } + else /* user output is not interleaved */ + { + for( i = 0; i < bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->bytesPerUserOutputSample * frameCount; + } + + userOutput = bp->tempOutputBufferPtrs; + } + } + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + frameCount, bp->timeInfo, + statusFlags, bp->userData ); + + bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; + bp->timeInfo->outputBufferDacTime += frameCount * bp->samplePeriod; + + + // FIXME: if streamCallback result is abort, then abort! + + /* convert output data (user -> host) */ + if( bp->outputChannelCount != 0 ) + { + /* + could use more elaborate logic here and sometimes process + buffers in-place. + */ + + srcBytePtr = bp->tempOutputBuffer; + + if( bp->userOutputIsInterleaved ) + { + srcStride = bp->outputChannelCount; + srcBytePtrStride = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcStride = 1; + srcBytePtrStride = frameCount * bp->bytesPerUserOutputSample; + } + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcStride, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcBytePtrStride; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + } + + framesProcessed += frameCount; + + framesToGo -= frameCount; + } + while( framesToGo > 0 ); + + return framesProcessed; +} + + +/* + AdaptingInputOnlyProcess() is a half duplex input buffer processor. It + converts data from the input buffers into the temporary input buffer, + when the temporary input buffer is full, it calls the streamCallback. +*/ +static unsigned long AdaptingInputOnlyProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostInputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *destBytePtr; + unsigned int destStride, destBytePtrStride; + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + unsigned long statusFlags = 0; // FIXME: implement this + + userOutput = 0; + + do + { + frameCount = ( bp->framesInTempInputBuffer + framesToGo > bp->framesPerUserBuffer ) + ? ( bp->framesPerUserBuffer - bp->framesInTempInputBuffer ) + : framesToGo; + + /* convert frameCount samples into temp buffer */ + + if( bp->userInputIsInterleaved ) + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->inputChannelCount * + bp->framesInTempInputBuffer; + + destStride = bp->inputChannelCount; + destBytePtrStride = bp->bytesPerUserInputSample; + + userInput = bp->tempInputBuffer; + } + else /* user input is not interleaved */ + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; + + destStride = 1; + destBytePtrStride = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + + /* setup non-interleaved ptrs */ + for( i=0; iinputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->bytesPerUserInputSample * bp->framesPerUserBuffer; + } + + userInput = bp->tempInputBufferPtrs; + } + + for( i=0; iinputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destStride, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destBytePtrStride; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + + bp->framesInTempInputBuffer += frameCount; + + if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer ) + { + bp->timeInfo->outputBufferDacTime = 0; + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + statusFlags, bp->userData ); + + bp->timeInfo->inputBufferAdcTime += frameCount * bp->samplePeriod; + + // FIXME: if streamCallback result is abort, then abort! + + bp->framesInTempInputBuffer = 0; + } + + framesProcessed += frameCount; + + framesToGo -= frameCount; + }while( framesToGo > 0 ); + + return framesProcessed; +} + + +/* + AdaptingOutputOnlyProcess() is a half duplex output buffer processor. + It converts data from the temporary output buffer, to the output buffers, + when the temporary output buffer is empty, it calls the streamCallback. +*/ +static unsigned long AdaptingOutputOnlyProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, + PaUtilChannelDescriptor *hostOutputChannels, + unsigned long framesToProcess ) +{ + void *userInput, *userOutput; + unsigned char *srcBytePtr; + unsigned int srcStride, srcBytePtrStride; + unsigned int i; + unsigned long frameCount; + unsigned long framesToGo = framesToProcess; + unsigned long framesProcessed = 0; + unsigned long statusFlags = 0; // FIXME: implement this + + do + { + if( bp->framesInTempOutputBuffer == 0 ) + { + userInput = 0; + + /* setup userOutput */ + if( bp->userOutputIsInterleaved ) + { + userOutput = bp->tempOutputBuffer; + } + else /* user output is not interleaved */ + { + for( i = 0; i < bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + userOutput = bp->tempOutputBufferPtrs; + } + + bp->timeInfo->inputBufferAdcTime = 0; + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + statusFlags, bp->userData ); + + bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; + + // FIXME: if streamCallback result is abort, then abort! + + bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; + } + + frameCount = ( bp->framesInTempOutputBuffer > framesToGo ) + ? framesToGo + : bp->framesInTempOutputBuffer; + + /* convert frameCount frames from user buffer to host buffer */ + + if( bp->userOutputIsInterleaved ) + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * bp->outputChannelCount * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcStride = bp->outputChannelCount; + srcBytePtrStride = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcStride = 1; + srcBytePtrStride = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcStride, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcBytePtrStride; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + bp->framesInTempOutputBuffer -= frameCount; + + framesProcessed += frameCount; + + framesToGo -= frameCount; + + }while( framesToGo > 0 ); + + return framesProcessed; +} + + +/* + AdaptingProcess is a full duplex adapting buffer processor. It converts + data from the temporary output buffer into the host output buffers, then + from the host input buffers into the temporary input buffers. Calling the + streamCallback when necessary. + When processPartialUserBuffers is 0, all available input data will be + consumed and all available output space will be filled. When + processPartialUserBuffers is non-zero, as many full user buffers + as possible will be processed, but partial buffers will not be consumed. +*/ +static unsigned long AdaptingProcess( PaUtilBufferProcessor *bp, + int *streamCallbackResult, int processPartialUserBuffers ) +{ + void *userInput, *userOutput; + unsigned long framesProcessed = 0; + unsigned long framesAvailable; + unsigned long endProcessingMinFrameCount; + unsigned long maxFramesToCopy; + PaUtilChannelDescriptor *hostInputChannels, *hostOutputChannels; + unsigned int frameCount; + unsigned char *srcBytePtr, *destBytePtr; + unsigned int srcStride, srcBytePtrStride, destStride, destBytePtrStride; + unsigned int i; + unsigned long statusFlags = 0; // FIXME: implement this + + framesAvailable = bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1];/* this is assumed to be the same as the output buffers frame count */ + + if( processPartialUserBuffers ) + endProcessingMinFrameCount = 0; + else + endProcessingMinFrameCount = (bp->framesPerUserBuffer - 1); + + while( framesAvailable > endProcessingMinFrameCount ) + { + /* copy frames from user to host output buffers */ + while( bp->framesInTempOutputBuffer > 0 && + ((bp->hostOutputFrameCount[0] + bp->hostOutputFrameCount[1]) > 0) ) + { + maxFramesToCopy = bp->framesInTempOutputBuffer; + + /* select the output buffer set (1st or 2nd) */ + if( bp->hostOutputFrameCount[0] > 0 ) + { + hostOutputChannels = bp->hostOutputChannels[0]; + frameCount = (bp->hostOutputFrameCount[0] < maxFramesToCopy) + ? bp->hostOutputFrameCount[0] + : maxFramesToCopy; + } + else + { + hostOutputChannels = bp->hostOutputChannels[1]; + frameCount = (bp->hostOutputFrameCount[1] < maxFramesToCopy) + ? bp->hostOutputFrameCount[1] + : maxFramesToCopy; + } + + if( bp->userOutputIsInterleaved ) + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * bp->outputChannelCount * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcStride = bp->outputChannelCount; + srcBytePtrStride = bp->bytesPerUserOutputSample; + } + else /* user output is not interleaved */ + { + srcBytePtr = ((unsigned char*)bp->tempOutputBuffer) + + bp->bytesPerUserOutputSample * + (bp->framesPerUserBuffer - bp->framesInTempOutputBuffer); + + srcStride = 1; + srcBytePtrStride = bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + for( i=0; ioutputChannelCount; ++i ) + { + bp->outputConverter( hostOutputChannels[i].data, + hostOutputChannels[i].stride, + srcBytePtr, srcStride, + frameCount, &bp->ditherGenerator ); + + srcBytePtr += srcBytePtrStride; /* skip to next source channel */ + + /* advance dest ptr for next iteration */ + hostOutputChannels[i].data = ((unsigned char*)hostOutputChannels[i].data) + + frameCount * hostOutputChannels[i].stride * bp->bytesPerHostOutputSample; + } + + if( bp->hostOutputFrameCount[0] > 0 ) + bp->hostOutputFrameCount[0] -= frameCount; + else + bp->hostOutputFrameCount[1] -= frameCount; + + bp->framesInTempOutputBuffer -= frameCount; + } + + + /* copy frames from host to user input buffers */ + while( bp->framesInTempInputBuffer < bp->framesPerUserBuffer && + ((bp->hostInputFrameCount[0] + bp->hostInputFrameCount[1]) > 0) ) + { + maxFramesToCopy = bp->framesPerUserBuffer - bp->framesInTempInputBuffer; + + /* select the input buffer set (1st or 2nd) */ + if( bp->hostInputFrameCount[0] > 0 ) + { + hostInputChannels = bp->hostInputChannels[0]; + frameCount = (bp->hostInputFrameCount[0] < maxFramesToCopy) + ? bp->hostInputFrameCount[0] + : maxFramesToCopy; + } + else + { + hostInputChannels = bp->hostInputChannels[1]; + frameCount = (bp->hostInputFrameCount[1] < maxFramesToCopy) + ? bp->hostInputFrameCount[1] + : maxFramesToCopy; + } + + /* configure conversion destination pointers */ + if( bp->userInputIsInterleaved ) + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->inputChannelCount * + bp->framesInTempInputBuffer; + + destStride = bp->inputChannelCount; + destBytePtrStride = bp->bytesPerUserInputSample; + } + else /* user input is not interleaved */ + { + destBytePtr = ((unsigned char*)bp->tempInputBuffer) + + bp->bytesPerUserInputSample * bp->framesInTempInputBuffer; + + destStride = 1; + destBytePtrStride = bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + } + + for( i=0; iinputChannelCount; ++i ) + { + bp->inputConverter( destBytePtr, destStride, + hostInputChannels[i].data, + hostInputChannels[i].stride, + frameCount, &bp->ditherGenerator ); + + destBytePtr += destBytePtrStride; /* skip to next destination channel */ + + /* advance src ptr for next iteration */ + hostInputChannels[i].data = ((unsigned char*)hostInputChannels[i].data) + + frameCount * hostInputChannels[i].stride * bp->bytesPerHostInputSample; + } + + if( bp->hostInputFrameCount[0] > 0 ) + bp->hostInputFrameCount[0] -= frameCount; + else + bp->hostInputFrameCount[1] -= frameCount; + + bp->framesInTempInputBuffer += frameCount; + + /* update framesAvailable and framesProcessed based on input consumed + unless something is very wrong this will also correspond to the + amount of output generated */ + framesAvailable -= frameCount; + framesProcessed += frameCount; + } + + /* call streamCallback */ + if( bp->framesInTempInputBuffer == bp->framesPerUserBuffer && + bp->framesInTempOutputBuffer == 0 ) + { + /* setup userInput */ + if( bp->userInputIsInterleaved ) + { + userInput = bp->tempInputBuffer; + } + else /* user input is not interleaved */ + { + for( i = 0; i < bp->inputChannelCount; ++i ) + { + bp->tempInputBufferPtrs[i] = ((unsigned char*)bp->tempInputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserInputSample; + } + + userInput = bp->tempInputBufferPtrs; + } + + /* setup userOutput */ + if( bp->userOutputIsInterleaved ) + { + userOutput = bp->tempOutputBuffer; + } + else /* user output is not interleaved */ + { + for( i = 0; i < bp->outputChannelCount; ++i ) + { + bp->tempOutputBufferPtrs[i] = ((unsigned char*)bp->tempOutputBuffer) + + i * bp->framesPerUserBuffer * bp->bytesPerUserOutputSample; + } + + userOutput = bp->tempOutputBufferPtrs; + } + + /* call streamCallback */ + + *streamCallbackResult = bp->streamCallback( userInput, userOutput, + bp->framesPerUserBuffer, bp->timeInfo, + statusFlags, bp->userData ); + + bp->timeInfo->inputBufferAdcTime += bp->framesPerUserBuffer * bp->samplePeriod; + bp->timeInfo->outputBufferDacTime += bp->framesPerUserBuffer * bp->samplePeriod; + + + // FIXME: if streamCallback result is abort, then abort! + + + bp->framesInTempInputBuffer = 0; + bp->framesInTempOutputBuffer = bp->framesPerUserBuffer; + } + } + + return framesProcessed; +} diff --git a/pd/portaudio/pa_common/pa_process.h b/pd/portaudio/pa_common/pa_process.h new file mode 100644 index 00000000..47bc0d70 --- /dev/null +++ b/pd/portaudio/pa_common/pa_process.h @@ -0,0 +1,203 @@ +#ifndef PA_PROCESS_H +#define PA_PROCESS_H +/* + * $Id: pa_process.h,v 1.1.2.16 2002/10/26 05:33:29 rossbencina Exp $ + * Portable Audio I/O Library callback buffer processing adapters + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Phil Burk, Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "portaudio.h" +#include "pa_converters.h" +#include "pa_dither.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/** @file + + @todo finish documentation for the buffer processor +*/ + +typedef enum { + paUtilFixedHostBufferSize, + paUtilBoundedHostBufferSize, + paUtilUnknownHostBufferSize, + paUtilVariableHostBufferSizePartialUsageAllowed, /**< the only mode where process() may not consume the whole buffer */ +}PaUtilHostBufferSizeMode; + + +typedef struct PaUtilChannelDescriptor{ + void *data; + unsigned int stride; +}PaUtilChannelDescriptor; + + +typedef struct { + unsigned long framesPerUserBuffer; + unsigned long framesPerHostBuffer; + + PaUtilHostBufferSizeMode hostBufferSizeMode; + int useNonAdaptingProcess; + unsigned long framesPerTempBuffer; + + unsigned int inputChannelCount; + unsigned int bytesPerHostInputSample; + unsigned int bytesPerUserInputSample; + int userInputIsInterleaved; + PaUtilConverter *inputConverter; + + unsigned int outputChannelCount; + unsigned int bytesPerHostOutputSample; + unsigned int bytesPerUserOutputSample; + int userOutputIsInterleaved; + PaUtilConverter *outputConverter; + + void *tempInputBuffer; /**< used for slips, block adaption, and conversion. */ + void **tempInputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user input */ + unsigned long framesInTempInputBuffer; /**< frames remaining in input buffer from previous adaption iteration */ + + void *tempOutputBuffer; /**< used for slips, block adaption, and conversion. */ + void **tempOutputBufferPtrs; /**< storage for non-interleaved buffer pointers, NULL for interleaved user output */ + unsigned long framesInTempOutputBuffer; /**< frames remaining in input buffer from previous adaption iteration */ + + PaStreamCallbackTimeInfo *timeInfo; + + unsigned long hostInputFrameCount[2]; + PaUtilChannelDescriptor *hostInputChannels[2]; + unsigned long hostOutputFrameCount[2]; + PaUtilChannelDescriptor *hostOutputChannels[2]; + + PaUtilTriangularDitherGenerator ditherGenerator; + + double samplePeriod; + + PaStreamCallback *streamCallback; + void *userData; +} PaUtilBufferProcessor; + + +/** + @param framesPerHostBuffer Specifies the number of frames per host buffer + for fixed the fixed buffer size mode, and the maximum number of frames + per host buffer for the bounded host buffer size mode. It is ignored for + the other modes. + + @note The interleave flag is ignored for host buffer formats. Host interleave + is determined by the use of different SetInput and SetOutput functions. +*/ +PaError PaUtil_InitializeBufferProcessor( PaUtilBufferProcessor* bufferProcessor, + int numInputChannels, PaSampleFormat userInputSampleFormat, + PaSampleFormat hostInputSampleFormat, + int numOutputChannels, PaSampleFormat userOutputSampleFormat, + PaSampleFormat hostOutputSampleFormat, + double sampleRate, + PaStreamFlags streamFlags, + unsigned long framesPerUserBuffer, /* 0 indicates don't care */ + unsigned long framesPerHostBuffer, + PaUtilHostBufferSizeMode hostBufferSizeMode, + PaStreamCallback *streamCallback, void *userData ); + + +void PaUtil_TerminateBufferProcessor( PaUtilBufferProcessor* bufferProcessor ); + + +/** + @param timeInfo Timing information for the first sample of the buffer(s) + passed to the buffer processor. The buffer processor may adjust this + information as necessary. +*/ +void PaUtil_BeginBufferProcessing( PaUtilBufferProcessor* bufferProcessor, + PaStreamCallbackTimeInfo* timeInfo ); + +/** returns the number of frames processed */ +unsigned long PaUtil_EndBufferProcessing( PaUtilBufferProcessor* bufferProcessor, int *callbackResult ); + + +/** a 0 frameCount indicates to use the framesPerHostBuffer value passed to init */ +void PaUtil_SetInputFrameCount( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + + +void PaUtil_SetInputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data, unsigned int stride ); + +/** if channel count is zero use all channels as specified to initialize buffer processor */ +void PaUtil_SetInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor, + unsigned int firstChannel, void *data, unsigned int channelCount ); + +void PaUtil_SetNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data ); + + +void PaUtil_Set2ndInputFrameCount( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + +void PaUtil_Set2ndInputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data, unsigned int stride ); + +/** if channel count is zero use all channels as specified to initialize buffer processor */ +void PaUtil_Set2ndInterleavedInputChannels( PaUtilBufferProcessor* bufferProcessor, + unsigned int firstChannel, void *data, unsigned int channelCount ); + +void PaUtil_Set2ndNonInterleavedInputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data ); + +/** a 0 frameCount indicates to use the framesPerHostBuffer value passed to init */ +void PaUtil_SetOutputFrameCount( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + +void PaUtil_SetOutputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data, unsigned int stride ); + +/** if channel count is zero use all channels as specified to initialize buffer processor */ +void PaUtil_SetInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor, + unsigned int firstChannel, void *data, unsigned int channelCount ); + +void PaUtil_SetNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data ); + + +void PaUtil_Set2ndOutputFrameCount( PaUtilBufferProcessor* bufferProcessor, + unsigned long frameCount ); + +void PaUtil_Set2ndOutputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data, unsigned int stride ); + +/** if channel count is zero use all channels as specified to initialize buffer processor */ +void PaUtil_Set2ndInterleavedOutputChannels( PaUtilBufferProcessor* bufferProcessor, + unsigned int firstChannel, void *data, unsigned int channelCount ); + +void PaUtil_Set2ndNonInterleavedOutputChannel( PaUtilBufferProcessor* bufferProcessor, + unsigned int channel, void *data ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_PROCESS_H */ diff --git a/pd/portaudio/pa_common/pa_process.o b/pd/portaudio/pa_common/pa_process.o new file mode 100644 index 00000000..3429227b Binary files /dev/null and b/pd/portaudio/pa_common/pa_process.o differ diff --git a/pd/portaudio/pa_common/pa_skeleton.c b/pd/portaudio/pa_common/pa_skeleton.c new file mode 100644 index 00000000..d9a90f4b --- /dev/null +++ b/pd/portaudio/pa_common/pa_skeleton.c @@ -0,0 +1,724 @@ +/* + * $Id: pa_skeleton.c,v 1.1.2.27 2002/12/03 06:30:40 rossbencina Exp $ + * Portable Audio I/O Library skeleton implementation + * demonstrates how to use the common functions to implement support + * for a host API + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include /* strlen() */ + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +/** @file + NOTE TO IMPLEMENTORS: + + This file is provided as a starting point for implementing support for + a new host API. IMPLEMENT ME comments are used to indicate functionality + which much be customised for each implementation. +*/ + + +/* prototypes for functions declared in this file */ + +PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ); +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamInputLatency( PaStream *stream ); +static PaTime GetStreamOutputLatency( 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, void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); + + +/* IMPLEMENT ME: a macro like the following one should be used for reporting + host errors */ +#define PA_SKELETON_SET_LAST_HOST_ERROR( errorCode, errorText ) \ + PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) + +/* PaSkeletonHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + /* implementation specific data goes here */ +} +PaSkeletonHostApiRepresentation; /* IMPLEMENT ME: rename this */ + + +PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + int i, deviceCount; + PaSkeletonHostApiRepresentation *skeletonHostApi; + PaDeviceInfo *deviceInfoArray; + + skeletonHostApi = (PaSkeletonHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaSkeletonHostApiRepresentation) ); + if( !skeletonHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + skeletonHostApi->allocations = PaUtil_CreateAllocationGroup(); + if( !skeletonHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + *hostApi = &skeletonHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paInDevelopment; /* IMPLEMENT ME: change to correct type id */ + (*hostApi)->info.name = "skeleton implementation"; /* IMPLEMENT ME: change to correct name */ + + (*hostApi)->info.defaultInputDevice = paNoDevice; /* IMPLEMENT ME */ + (*hostApi)->info.defaultOutputDevice = paNoDevice; /* IMPLEMENT ME */ + + (*hostApi)->info.deviceCount = 0; + + deviceCount = 0; /* IMPLEMENT ME */ + + if( deviceCount > 0 ) + { + (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + skeletonHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ); + if( !(*hostApi)->deviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( + skeletonHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount ); + if( !deviceInfoArray ) + { + result = paInsufficientMemory; + goto error; + } + + for( i=0; i < deviceCount; ++i ) + { + PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + deviceInfo->name = 0; /* IMPLEMENT ME: allocate block and copy name eg: + deviceName = (char*)PaUtil_GroupAllocateMemory( skeletonHostApi->allocations, strlen(srcName) + 1 ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, srcName ); + deviceInfo->name = deviceName; + */ + + deviceInfo->maxInputChannels = 0; /* IMPLEMENT ME */ + deviceInfo->maxOutputChannels = 0; /* IMPLEMENT ME */ + + deviceInfo->defaultLowInputLatency = 0.; /* IMPLEMENT ME */ + deviceInfo->defaultLowOutputLatency = 0.; /* IMPLEMENT ME */ + deviceInfo->defaultHighInputLatency = 0.; /* IMPLEMENT ME */ + deviceInfo->defaultHighOutputLatency = 0.; /* IMPLEMENT ME */ + + deviceInfo->defaultSampleRate = 0.; /* IMPLEMENT ME */ + + (*hostApi)->deviceInfos[i] = deviceInfo; + ++(*hostApi)->info.deviceCount; + } + } + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyReadWrite, PaUtil_DummyReadWrite, PaUtil_DummyGetAvailable, PaUtil_DummyGetAvailable ); + + PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + return result; + +error: + if( skeletonHostApi ) + { + if( skeletonHostApi->allocations ) + { + PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); + PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); + } + + PaUtil_FreeMemory( skeletonHostApi ); + } + return result; +} + + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi; + + /* + IMPLEMENT ME: + - clean up any resourced not handled by the allocation group + */ + + if( skeletonHostApi->allocations ) + { + PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); + PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); + } + + PaUtil_FreeMemory( skeletonHostApi ); +} + + +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + } + + /* + IMPLEMENT ME: + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + */ + + return paFormatIsSupported; +} + +/* PaSkeletonStream - a stream data structure specifically for this implementation */ + +typedef struct PaSkeletonStream +{ /* IMPLEMENT ME: rename this */ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + /* IMPLEMENT ME: + - implementation specific data goes here + */ + unsigned long framesPerHostCallback; /* just an example */ +} +PaSkeletonStream; + +/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ + +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ) +{ + PaError result = paNoError; + PaSkeletonHostApiRepresentation *skeletonHostApi = (PaSkeletonHostApiRepresentation*)hostApi; + PaSkeletonStream *stream = 0; + unsigned long framesPerHostBuffer = framesPerBuffer; /* these may not be equivalent for all implementations */ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat=0, outputSampleFormat=0; + PaSampleFormat hostInputSampleFormat=0, hostOutputSampleFormat=0; + + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + + /* IMPLEMENT ME - establish which host formats are available */ + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + + /* IMPLEMENT ME - establish which host formats are available */ + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); + } + else + { + outputChannelCount = 0; + } + + /* + IMPLEMENT ME: + + ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() FIXME - checks needed? ) + + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + + - alter sampleRate to a close allowable rate if possible / necessary + + - validate suggestedInputLatency and suggestedOutputLatency parameters, + use default values where necessary + */ + + + + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + + stream = (PaSkeletonStream*)PaUtil_AllocateMemory( sizeof(PaSkeletonStream) ); + if( !stream ) + { + result = paInsufficientMemory; + goto error; + } + + if( streamCallback ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &skeletonHostApi->callbackStreamInterface, streamCallback, userData ); + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &skeletonHostApi->blockingStreamInterface, streamCallback, userData ); + } + + /* + IMPLEMENT ME: initialise the following fields with estimated or actual + values. + */ + stream->streamRepresentation.streamInfo.inputLatency = 0.; + stream->streamRepresentation.streamInfo.outputLatency = 0.; + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + + /* we assume a fixed host buffer size in this example, but the buffer processor + can also support bounded and unknown host buffer sizes by passing + paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of + paUtilFixedHostBufferSize below. */ + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, hostInputSampleFormat, + outputChannelCount, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, + framesPerHostBuffer, paUtilFixedHostBufferSize, + streamCallback, userData ); + if( result != paNoError ) + goto error; + + /* + IMPLEMENT ME: + - additional stream setup + opening + */ + + stream->framesPerHostCallback = framesPerHostBuffer; + + *s = (PaStream*)stream; + + return result; + +error: + if( stream ) + PaUtil_FreeMemory( stream ); + + return result; +} + +/* + ExampleHostProcessingLoop() illustrates the kind of processing which may + occur in a host implementation. + +*/ +static void ExampleHostProcessingLoop( void *inputBuffer, void *outputBuffer, void *userData ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)userData; + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */ + int callbackResult; + unsigned long framesProcessed; + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + /* + IMPLEMENT ME: + - generate timing information + - handle buffer slips + */ + + /* + If you need to byte swap or shift inputBuffer to convert it into a + portaudio format, do it here. + */ + + + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo ); + + /* + depending on whether the host buffers are interleaved, non-interleaved + or a mixture, you will want to call PaUtil_SetInterleaved*Channels(), + PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here. + */ + + PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, + 0, /* first channel of inputBuffer is channel 0 */ + inputBuffer, + 0 ); /* 0 - use inputChannelCount passed to init buffer processor */ + + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, + 0, /* first channel of outputBuffer is channel 0 */ + outputBuffer, + 0 ); /* 0 - use outputChannelCount passed to init buffer processor */ + + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + + + /* + If you need to byte swap or shift outputBuffer to convert it to + host format, do it here. + */ + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + + + if( callbackResult == paContinue ) + { + /* nothing special to do */ + } + else if( callbackResult == paAbort ) + { + /* IMPLEMENT ME - finish playback immediately */ + + /* once finished, call the finished callback */ + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + } + else + { + /* User callback has asked us to stop with paComplete or other non-zero value */ + + /* IMPLEMENT ME - finish playback once currently queued audio has completed */ + + /* once finished, call the finished callback */ + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + } +} + + +/* + When CloseStream() is called, the multi-api layer ensures that + the stream has already been stopped or aborted. +*/ +static PaError CloseStream( PaStream* s ) +{ + PaError result = paNoError; + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* + IMPLEMENT ME: + - additional stream closing + cleanup + */ + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + + return result; +} + + +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return result; +} + + +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return result; +} + + +static PaError AbortStream( PaStream *s ) +{ + PaError result = paNoError; + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return result; +} + + +static PaError IsStreamStopped( PaStream *s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return 0; +} + + +static PaError IsStreamActive( PaStream *s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior */ + + return 0; +} + + +static PaTime GetStreamTime( PaStream *s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + + +/* + As separate stream interfaces are used for blocking and callback + streams, the following functions can be guaranteed to only be called + for blocking streams. +*/ + +static PaError ReadStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return paNoError; +} + + +static PaError WriteStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return paNoError; +} + + +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaSkeletonStream *stream = (PaSkeletonStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + + diff --git a/pd/portaudio/pa_common/pa_skeleton.o b/pd/portaudio/pa_common/pa_skeleton.o new file mode 100644 index 00000000..4641670a Binary files /dev/null and b/pd/portaudio/pa_common/pa_skeleton.o differ diff --git a/pd/portaudio/pa_common/pa_stream.c b/pd/portaudio/pa_common/pa_stream.c new file mode 100644 index 00000000..b2eba53d --- /dev/null +++ b/pd/portaudio/pa_common/pa_stream.c @@ -0,0 +1,114 @@ +/* + * $Id: pa_stream.c,v 1.1.2.9 2002/12/03 06:30:40 rossbencina Exp $ + * Portable Audio I/O Library + * + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 2002 Ross Bencina + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "pa_stream.h" + +void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface, + PaError (*Close)( PaStream* ), + PaError (*Start)( PaStream* ), + PaError (*Stop)( PaStream* ), + PaError (*Abort)( PaStream* ), + PaError (*IsStopped)( PaStream* ), + PaError (*IsActive)( PaStream* ), + PaTime (*GetTime)( PaStream* ), + double (*GetCpuLoad)( PaStream* ), + PaError (*Read)( PaStream*, void *, unsigned long ), + PaError (*Write)( PaStream*, void *, unsigned long ), + signed long (*GetReadAvailable)( PaStream* ), + signed long (*GetWriteAvailable)( PaStream* ) ) +{ + streamInterface->Close = Close; + streamInterface->Start = Start; + streamInterface->Stop = Stop; + streamInterface->Abort = Abort; + streamInterface->IsStopped = IsStopped; + streamInterface->IsActive = IsActive; + streamInterface->GetTime = GetTime; + streamInterface->GetCpuLoad = GetCpuLoad; + streamInterface->Read = Read; + streamInterface->Write = Write; + streamInterface->GetReadAvailable = GetReadAvailable; + streamInterface->GetWriteAvailable = GetWriteAvailable; +} + + +void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation, + PaUtilStreamInterface *streamInterface, + PaStreamCallback *streamCallback, + void *userData ) +{ + streamRepresentation->magic = PA_STREAM_MAGIC; + streamRepresentation->nextOpenStream = 0; + streamRepresentation->streamInterface = streamInterface; + streamRepresentation->streamCallback = streamCallback; + streamRepresentation->streamFinishedCallback = 0; + + streamRepresentation->userData = userData; + + streamRepresentation->streamInfo.inputLatency = 0.; + streamRepresentation->streamInfo.outputLatency = 0.; + streamRepresentation->streamInfo.sampleRate = 0.; +} + + +void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation ) +{ + streamRepresentation->magic = 0; +} + + +PaError PaUtil_DummyReadWrite( PaStream* stream, + void *buffer, + unsigned long frames ) +{ + (void)stream; /* unused parameter */ + (void)buffer; /* unused parameter */ + (void)frames; /* unused parameter */ + + return paNoError; /* @todo FIXME: need new error code paCantReadWriteToCallbackStream or something */ +} + + +signed long PaUtil_DummyGetAvailable( PaStream* stream ) +{ + (void)stream; /* unused parameter */ + + return 0; +} + + +double PaUtil_DummyGetCpuLoad( PaStream* stream ) +{ + (void)stream; /* unused parameter */ + + return 0.0; +} diff --git a/pd/portaudio/pa_common/pa_stream.h b/pd/portaudio/pa_common/pa_stream.h new file mode 100644 index 00000000..5e66c097 --- /dev/null +++ b/pd/portaudio/pa_common/pa_stream.h @@ -0,0 +1,128 @@ +#ifndef PA_STREAM_H +#define PA_STREAM_H +/* + * $Id: pa_stream.h,v 1.1.2.9 2002/12/03 06:30:40 rossbencina Exp $ + * Portable Audio I/O Library + * stream interface + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** @file + Interface used by pa_front to virtualise stream calls. +*/ + +#include "portaudio.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +#define PA_STREAM_MAGIC (0x18273645) + +typedef struct { + /* + all PaStreamInterface functions are guaranteed to be called with a + non-null, valid parameter + */ + PaError (*Close)( PaStream* stream ); + PaError (*Start)( PaStream *stream ); + PaError (*Stop)( PaStream *stream ); + PaError (*Abort)( PaStream *stream ); + PaError (*IsStopped)( PaStream *stream ); + PaError (*IsActive)( PaStream *stream ); + PaTime (*GetTime)( PaStream *stream ); + double (*GetCpuLoad)( PaStream* stream ); + PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ); + PaError (*Write)( PaStream* stream, void *buffer, unsigned long frames ); + signed long (*GetReadAvailable)( PaStream* stream ); + signed long (*GetWriteAvailable)( PaStream* stream ); +} PaUtilStreamInterface; + + +void PaUtil_InitializeStreamInterface( PaUtilStreamInterface *streamInterface, + PaError (*Close)( PaStream* ), + PaError (*Start)( PaStream* ), + PaError (*Stop)( PaStream* ), + PaError (*Abort)( PaStream* ), + PaError (*IsStopped)( PaStream* ), + PaError (*IsActive)( PaStream* ), + PaTime (*GetTime)( PaStream* ), + double (*GetCpuLoad)( PaStream* ), + PaError (*Read)( PaStream* stream, void *buffer, unsigned long frames ), + PaError (*Write)( PaStream* stream, void *buffer, unsigned long frames ), + signed long (*GetReadAvailable)( PaStream* stream ), + signed long (*GetWriteAvailable)( PaStream* stream ) ); + + +/** Use PaUtil_DummyReadWrite and PaUtil_DummyGetAvailable for + callback based streams. +*/ +PaError PaUtil_DummyReadWrite( PaStream* stream, + void *buffer, + unsigned long frames ); + + +signed long PaUtil_DummyGetAvailable( PaStream* stream ); + +/** Use PaUtil_DummyGetCpuLoad for read/write streams +*/ +double PaUtil_DummyGetCpuLoad( PaStream* stream ); + + +typedef struct PaUtilStreamRepresentation { + unsigned long magic; /* set to PA_STREAM_MAGIC */ + struct PaUtilStreamRepresentation *nextOpenStream; /* field used by multi-api code */ + PaUtilStreamInterface *streamInterface; + PaStreamCallback *streamCallback; + PaStreamFinishedCallback *streamFinishedCallback; + void *userData; + PaStreamInfo streamInfo; +} PaUtilStreamRepresentation; + + +void PaUtil_InitializeStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation, + PaUtilStreamInterface *streamInterface, + PaStreamCallback *streamCallback, + void *userData ); + +void PaUtil_TerminateStreamRepresentation( PaUtilStreamRepresentation *streamRepresentation ); + + +#define PA_STREAM_REP( streamRepPtr )\ + ((PaUtilStreamRepresentation*) streamRepPtr ) + +#define PA_STREAM_INTERFACE( streamRepPtr )\ + PA_STREAM_REP( streamRepPtr )->streamInterface + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_STREAM_H */ diff --git a/pd/portaudio/pa_common/pa_stream.o b/pd/portaudio/pa_common/pa_stream.o new file mode 100644 index 00000000..c75789bb Binary files /dev/null and b/pd/portaudio/pa_common/pa_stream.o differ diff --git a/pd/portaudio/pa_common/pa_trace.c b/pd/portaudio/pa_common/pa_trace.c index d55a6d37..74e8bb2f 100644 --- a/pd/portaudio/pa_common/pa_trace.c +++ b/pd/portaudio/pa_common/pa_trace.c @@ -1,5 +1,5 @@ /* - * $Id: pa_trace.c,v 1.1.1.1 2002/01/22 00:52:11 phil Exp $ + * $Id: pa_trace.c,v 1.1.1.1.2.2 2002/10/26 05:34:03 rossbencina Exp $ * Portable Audio I/O Library Trace Facility * Store trace information in real-time for later printing. * @@ -35,7 +35,7 @@ #include #include "pa_trace.h" -#if TRACE_REALTIME_EVENTS +#if PA_TRACE_REALTIME_EVENTS static char *traceTextArray[MAX_TRACE_RECORDS]; static int traceIntArray[MAX_TRACE_RECORDS]; @@ -43,19 +43,19 @@ static int traceIndex = 0; static int traceBlock = 0; /*********************************************************************/ -void ResetTraceMessages() +void PaUtil_ResetTraceMessages() { traceIndex = 0; } /*********************************************************************/ -void DumpTraceMessages() +void PaUtil_DumpTraceMessages() { int i; - int numDump = (traceIndex < MAX_TRACE_RECORDS) ? traceIndex : MAX_TRACE_RECORDS; + int messageCount = (traceIndex < PA_MAX_TRACE_RECORDS) ? traceIndex : PA_MAX_TRACE_RECORDS; printf("DumpTraceMessages: traceIndex = %d\n", traceIndex ); - for( i=0; ideviceCount-1) + + @see PaHostApiInfo +*/ +PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, + int hostApiDeviceIndex ); + + + +/** Structure used to return information about a host error condition. +*/ +typedef struct PaHostErrorInfo{ + PaHostApiTypeId hostApiType; /**< the host API which returned the error code */ + long errorCode; /**< the error code returned */ + const char *errorText; /**< a textual description of the error if available, otherwise a zero-length string */ +}PaHostErrorInfo; + + +/** Return information about the last host error encountered. The error + information returned by Pa_GetLastHostErrorInfo() will never be modified + asyncronously by errors occurring in other PortAudio owned threads + (such as the thread that manages the stream callback.) + + This function is provided as a last resort, primarily to enhance debugging + by providing clients with access to all available error information. + + @return A pointer to an immutable structure constaining information about + the host error. The values in this structure will only be valid if a + PortAudio function has previously returned the paUnanticipatedHostError + error code. +*/ +const PaHostErrorInfo* Pa_GetLastHostErrorInfo( void ); + + + +/* Device enumeration and capabilities */ + +/** Retrieve the number of available devices. + @return The number of available devices. May return 0 if PortAudio is + not initialized or an error has occured. +*/ +PaDeviceIndex Pa_CountDevices( void ); + + +/** Retrieve the index of the default input device. The result can be + used in the inputDevice parameter to Pa_OpenStream(). + + @return The default input device index for the defualt host API, or paNoDevice + if no default input device is available or an error was encountered. +*/ +PaDeviceIndex Pa_GetDefaultInputDevice( void ); + + +/** Retrieve the index of the default output device. The result can be + used in the outputDevice parameter to Pa_OpenStream(). + + @return The default output device index for the defualt host API, or paNoDevice + if no default output device is available or an error was encountered. + + @note On the PC, the user can specify a default device by setting an environment variable. For example, to use device #1. - - set PA_RECOMMENDED_OUTPUT_DEVICE=1 - +
    + set PA_RECOMMENDED_OUTPUT_DEVICE=1
    +
    The user should first determine the available device ids by using the supplied application "pa_devs". +*/ +PaDeviceIndex Pa_GetDefaultOutputDevice( void ); + +/** The type used to represent monotonic time in seconds that can be used + for syncronisation. The type is used for the outTime argument to the + PaStreamCallback and as the result of Pa_GetStreamTime(). + + @see PaStreamCallback, Pa_GetStreamTime */ +typedef double PaTime; -PaDeviceID Pa_GetDefaultInputDeviceID( void ); -PaDeviceID Pa_GetDefaultOutputDeviceID( void ); +/** A type used to specify one or more sample formats. They indicate + the formats used to pass sound data between the stream callback and the + stream. Each device has one or more "native" formats which may be used when + optimum efficiency or control over conversion is required. + Formats marked "always available" are supported (emulated) by all + PortAudio implementations. -/* - Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure - for the device specified. - If the device parameter is out of range the function returns NULL. + The floating point representation (paFloat32) uses +1.0 and -1.0 as the + maximum and minimum respectively. - PortAudio manages the memory referenced by the returned pointer, the client - must not manipulate or free the memory. The pointer is only guaranteed to be - valid between calls to Pa_Initialize() and Pa_Terminate(). + paUInt8 is an unsigned 8 bit format where 128 is considered "ground" + + The paNonInterleaved flag indicates that a multichannel buffer is passed + as a set of non-interleaved pointers. + @see Pa_OpenStream, Pa_OpenDefaultStream, PaDeviceInfo + @see paFloat32, paInt16, paInt32, paInt24, paInt8 + @see paUInt8, paCustomFormat, paNonInterleaved */ +typedef unsigned long PaSampleFormat; -const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID device ); -/* - PaTimestamp is used to represent a continuous sample clock with arbitrary - start time that can be used for syncronization. The type is used for the - outTime argument to the PortAudioCallback and as the result of Pa_StreamTime() +#define paFloat32 ((PaSampleFormat) 0x00000001) /**< @see PaSampleFormat */ +#define paInt32 ((PaSampleFormat) 0x00000002) /**< @see PaSampleFormat */ +#define paInt24 ((PaSampleFormat) 0x00000004) /**< Packed 24 bit format. @see PaSampleFormat */ +#define paInt16 ((PaSampleFormat) 0x00000008) /**< @see PaSampleFormat */ +#define paInt8 ((PaSampleFormat) 0x00000010) /**< @see PaSampleFormat */ +#define paUInt8 ((PaSampleFormat) 0x00000020) /**< @see PaSampleFormat */ +#define paCustomFormat ((PaSampleFormat) 0x00010000)/**< @see PaSampleFormat */ + +#define paNonInterleaved ((PaSampleFormat) 0x80000000) +/** A structure providing information and capabilities of PortAudio devices. + Devices may support input, output or both input and output. */ +typedef struct PaDeviceInfo +{ + int structVersion; /* this is struct version 2 */ + const char *name; + PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/ + + int maxInputChannels; + int maxOutputChannels; -typedef double PaTimestamp; + /* Default latency values for interactive performance. */ + PaTime defaultLowInputLatency; + PaTime defaultLowOutputLatency; + /* Default latency values for robust non-interactive applications (eg. playing sound files). */ + PaTime defaultHighInputLatency; + PaTime defaultHighOutputLatency; -/* - PortAudioCallback is implemented by PortAudio clients. - - inputBuffer and outputBuffer are arrays of interleaved samples, - the format, packing and number of channels used by the buffers are - determined by parameters to Pa_OpenStream() (see below). - - framesPerBuffer is the number of sample frames to be processed by the callback. - - outTime is the time in samples when the buffer(s) processed by - this callback will begin being played at the audio output. - See also Pa_StreamTime() - - userData is the value of a user supplied pointer passed to Pa_OpenStream() - intended for storing synthesis data etc. - - return value: - The callback can return a non-zero value to stop the stream. This may be - useful in applications such as soundfile players where a specific duration - of output is required. However, it is not necessary to utilise this mechanism - as StopStream() will also terminate the stream. A callback returning a - non-zero value must fill the entire outputBuffer. - - NOTE: None of the other stream functions may be called from within the - callback function except for Pa_GetCPULoad(). + double defaultSampleRate; +} PaDeviceInfo; + + +/** Retrieve a pointer to a PaDeviceInfo structure containing information + about the specified device. + @return A pointer to an immutable PaDeviceInfo structure. If the device + parameter is out of range the function returns NULL. + + @param device A valid device index in the range 0 to (Pa_CountDevices()-1) + @note PortAudio manages the memory referenced by the returned pointer, + the client must not manipulate or free the memory. The pointer is only + guaranteed to be valid between calls to Pa_Initialize() and Pa_Terminate(). + + @see PaDeviceInfo, PaDeviceIndex */ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device ); -typedef int (PortAudioCallback)( - void *inputBuffer, void *outputBuffer, - unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ); +/** Parameters for one direction (input or output) of a stream. +*/ +typedef struct PaStreamParameters +{ + /** A valid device index in the range 0 to (Pa_CountDevices()-1) + specifying the device to be used or the special constant + paUseHostApiSpecificDeviceSpecification which indicates that the actual + device(s) to use are specified in hostApiSpecificStreamInfo. + This field must not be set to paNoDevice. + */ + PaDeviceIndex device; + + /** The number of channels of sound to be delivered to the + stream callback or accessed by Pa_ReadStream() or Pa_WriteStream(). + It can range from 1 to the value of maxInputChannels in the + PaDeviceInfo record for the device specified by the device parameter. + */ + int channelCount; + + /** The sample format of the buffer provided to the stream callback, + a_ReadStream() or Pa_WriteStream(). It may be any of the formats described + by the PaSampleFormat enumeration. + FIXME: wrt below, what are we guaranteeing these days, if anything? + PortAudio guarantees support for + the device's native formats (nativeSampleFormats in the device info record) + and additionally 16 and 32 bit integer and 32 bit floating point formats. + Support for other formats is implementation defined. + */ + PaSampleFormat sampleFormat; + + /** The desired latency in seconds. Where practical, implementations should + configure their latency based on these parameters, otherwise they may + choose the closest viable latency instead. Unless the suggested latency + is greater than the absolute upper limit for the device implementations + shouldround the suggestedLatency up to the next practial value - ie to + provide an equal or higher latency than suggestedLatency whereever possibe. + Actual latency values for an open stream may be retrieved using the + inputLatency and outputLatency fields of the PaStreamInfo structure + returned by Pa_GetStreamInfo(). + @see default*Latency in PaDeviceInfo, *Latency in PaStreamInfo + */ + PaTime suggestedLatency; + + /** An optional pointer to a host api specific data structure + containing additional information for device setup and/or stream processing. + hostApiSpecificStreamInfo is never required for correct operation. + If not used it should be set to paNullHostApiSpecificStreamInfo (aka NULL) + FIXME: redocument this based on new changes: + If hostApiSpecificStreamInfo is supplied, it's + size and hostApi fields must be compatible with the input devices host api. + */ + void *hostApiSpecificStreamInfo; + +} PaStreamParameters; + + +/** Return code for Pa_IsFormatSupported indicating success. */ +#define paFormatIsSupported (0) + +/** Determine whether it would be possible to open a stream with the specified + parameters. + + @param inputParameters A structure that describes the input parameters used to + open a stream. The suggestedLatency field is ignored. See PaStreamParameters + for a description of these parameters. inputParameters must be NULL for + output-only streams. + + @param outputParameters A structure that describes the output parameters used + to open a stream. The suggestedLatency field is ignored. See PaStreamParameters + for a description of these parameters. outputParameters must be NULL for + input-only streams. + + @param sampleRate The required sampleRate. For full-duplex streams it is the + sample rate for both input and output -/* - Stream flags - - These flags may be supplied (ored together) in the streamFlags argument to - the Pa_OpenStream() function. + @return Returns 0 if the format is supported, and an error code indicating why + the format is not supported otherwise. The constant paFormatIsSupported is + provided to compare with the return value for success. + @see paFormatIsSupported, PaStreamParameters */ +PaError Pa_IsFormatSupported( const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); -#define paNoFlag (0) -#define paClipOff (1<<0) /* disable default clipping of out of range samples */ -#define paDitherOff (1<<1) /* disable default dithering */ -#define paPlatformSpecificFlags (0x00010000) -typedef unsigned long PaStreamFlags; -/* - A single PortAudioStream provides multiple channels of real-time - input and output audio streaming to a client application. - Pointers to PortAudioStream objects are passed between PortAudio functions. + +/* Streaming types and functions */ + + +/** + A single PaStream can provide multiple channels of real-time + streaming audio input and output to a client application. A stream + provides access to audio hardware represented by one or more + PaDevices. Depending on the underlying Host API, it may be possible + to open multiple streams using the same device, however this behavior + is implementation defined. Portable applications should assume that + a PaDevice may be simultaneously used by at most one PaStream. + + Pointers to PaStream objects are passed between PortAudio functions that + operate on streams. + + @see Pa_OpenStream, Pa_OpenDefaultStream, Pa_OpenDefaultStream, Pa_CloseStream, + Pa_StartStream, Pa_StopStream, Pa_AbortStream, Pa_IsStreamActive, + Pa_GetStreamTime, Pa_GetStreamCpuLoad + */ +typedef void PaStream; -typedef void PortAudioStream; -#define PaStream PortAudioStream -/* - Pa_OpenStream() opens a stream for either input, output or both. - - stream is the address of a PortAudioStream pointer which will receive +/** Can be passed as the framesPerBuffer parameter to Pa_OpenStream() + or Pa_OpenDefaultStream() to indicate that the stream callback will + accept buffers of any size. +*/ +#define paFramesPerBufferUnspecified (0) + + +/** Flags used to control the behavior of a stream. They are passed as + parameters to Pa_OpenStream or Pa_OpenDefaultStream. Multiple flags may be + ORed together. + + @see Pa_OpenStream, Pa_OpenDefaultStream + @see paNoFlag, paClipOff, paDitherOff, paNeverDropInput, + paPrimeOutputBuffersUsingStreamCallback, paPlatformSpecificFlags +*/ +typedef unsigned long PaStreamFlags; + +/** @see PaStreamFlags */ +#define paNoFlag ((PaStreamFlags) 0) + +/** Disable default clipping of out of range samples. + @see PaStreamFlags +*/ +#define paClipOff ((PaStreamFlags) 0x00000001) + +/** Disable default dithering. + @see PaStreamFlags +*/ +#define paDitherOff ((PaStreamFlags) 0x00000002) + +/** A full duplex stream will not discard overflowed input samples without + calling the stream callback, this flag is ignored for blocking read/write + streams. + @see PaStreamFlags +*/ +#define paNeverDropInput ((PaStreamFlags) 0x00000004) + +/** Call the stream callback to fill initial output buffers, rather than the + default behavior of priming the buffers with zeros (silence). This flag has + no effect for input-only and blocking read/write streams. + @see PaStreamFlags +*/ +#define paPrimeOutputBuffersUsingStreamCallback ((PaStreamFlags) 0x00000008) + +/** A mask specifying the platform specific bits. + @see PaStreamFlags +*/ +#define paPlatformSpecificFlags ((PaStreamFlags)0xFFFF0000) + +/** + Timing information for the buffers passed to the stream callback. +*/ +typedef struct PaStreamCallbackTimeInfo{ + PaTime inputBufferAdcTime; + PaTime currentTime; + PaTime outputBufferDacTime; +} PaStreamCallbackTimeInfo; + + +/** + Flag bit constants for the statusFlags to PaStreamCallback. + + @see paInputUnderflow, paInputOverflow, paOutputUnderflow, paOutputOverflow, + paPrimingOutput +*/ +typedef unsigned long PaStreamCallbackFlags; + +/** Input data is all zeros because no real data is available. + @see PaStreamCallbackFlags +*/ +#define paInputUnderflow ((PaStreamCallbackFlags) 0x00000001) + +/** Input data was discarded by PortAudio + @see PaStreamCallbackFlags +*/ +#define paInputOverflow ((PaStreamCallbackFlags) 0x00000002) + +/** Output data was inserted by PortAudio because the stream callback is using + too much CPU + @see PaStreamCallbackFlags +*/ +#define paOutputUnderflow ((PaStreamCallbackFlags) 0x00000004) + +/** Output data will be discarded because no room is available. + @see PaStreamCallbackFlags +*/ +#define paOutputOverflow ((PaStreamCallbackFlags) 0x00000008) + +/** Some of all of the output data will be used to prime the stream, input + data may be zero. + @see PaStreamCallbackFlags +*/ +#define paPrimingOutput ((PaStreamCallbackFlags) 0x00000010) + +/** + Allowable return values for the PaStreamCallback. + @see PaStreamCallback +*/ +typedef enum PaStreamCallbackResult +{ + paContinue=0, + paComplete=1, + paAbort=2 +} PaStreamCallbackResult; + + +/** + Functions of type PaStreamCallback are implemented by PortAudio clients. + They consume, process or generate audio in response to requests from an + active PortAudio stream. + + @param input and @param output are arrays of interleaved samples, + the format, packing and number of channels used by the buffers are + determined by parameters to Pa_OpenStream(). + + @param frameCount The number of sample frames to be processed by + the stream callback. + + @param timeInfo The time in seconds when the first sample of the input + buffer was received at the audio input, the time in seconds when the first + sample of the output buffer will begin being played at the audio output, and + the time in seconds when the stream callback was called. + See also Pa_GetStreamTime() + + @param statusFlags Flags indicating whether input and/or output buffers + have been inserted or will be dropped to overcome underflow or overflow + conditions. + + @param userData The value of a user supplied pointer passed to + Pa_OpenStream() intended for storing synthesis data etc. + + @return + The stream callback should return one of the values in the + PaStreamCallbackResult enumeration. To ensure that the callback is continues + to be called, it should return paContinue (0). Either paComplete or paAbort + can be returned to finish stream processing, after either of these values is + returned the callback will not be called again. If paAbort is returned the + stream will finish as soon as possible. If paComplete is returned, the stream + will continue until all buffers generated by the callback have been played. + This may be useful in applications such as soundfile players where a specific + duration of output is required. However, it is not necessary to utilise this + mechanism as Pa_StopStream(), Pa_AbortStream() or Pa_CloseStream() can also + be used to stop the stream. The callback must always fill the entire output + buffer irrespective of its return value. + + @see Pa_OpenStream, Pa_OpenDefaultStream + + @note With the exception of Pa_GetStreamCpuLoad() it is not permissable to call + PortAudio API functions from within the stream callback. +*/ +typedef int PaStreamCallback( + void *input, void *output, + unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); + + +/** Opens a stream for either input, output or both. + + @param stream The address of a PaStream pointer which will receive a pointer to the newly opened stream. + + @param inputParameters A structure that describes the input parameters used by + the opened stream. See PaStreamParameters for a description of these parameters. + inputParameters must be NULL for output-only streams. + + @param outputParameters A structure that describes the output parameters used by + the opened stream. See PaStreamParameters for a description of these parameters. + outputParameters must be NULL for input-only streams. - inputDevice is the id of the device used for input (see PaDeviceID above.) - inputDevice may be paNoDevice to indicate that an input device is not required. - - numInputChannels is the number of channels of sound to be delivered to the - callback. It can range from 1 to the value of maxInputChannels in the - PaDeviceInfo record for the device specified by the inputDevice parameter. - If inputDevice is paNoDevice numInputChannels is ignored. - - inputSampleFormat is the sample format of inputBuffer provided to the callback - function. inputSampleFormat may be any of the formats described by the - PaSampleFormat enumeration (see above). PortAudio guarantees support for - the device's native formats (nativeSampleFormats in the device info record) - and additionally 16 and 32 bit integer and 32 bit floating point formats. - Support for other formats is implementation defined. - - inputDriverInfo is a pointer to an optional driver specific data structure - containing additional information for device setup or stream processing. - inputDriverInfo is never required for correct operation. If not used - inputDriverInfo should be NULL. - - outputDevice is the id of the device used for output (see PaDeviceID above.) - outputDevice may be paNoDevice to indicate that an output device is not required. - - numOutputChannels is the number of channels of sound to be supplied by the - callback. See the definition of numInputChannels above for more details. - - outputSampleFormat is the sample format of the outputBuffer filled by the - callback function. See the definition of inputSampleFormat above for more - details. - - outputDriverInfo is a pointer to an optional driver specific data structure - containing additional information for device setup or stream processing. - outputDriverInfo is never required for correct operation. If not used - outputDriverInfo should be NULL. - - sampleRate is the desired sampleRate. For full-duplex streams it is the + @param sampleRate The desired sampleRate. For full-duplex streams it is the sample rate for both input and output + + @param framesPerBuffer The number of frames passed to the stream callback + function, or the preferred block granularity for a blocking read/write stream. + The special value paFramesPerBufferUnspecified (0) may be used to request that + the stream callback will recieve an optimal (and possibly varying) number of + frames based on host requirements and the requested latency settings. + Note: With some host APIs, the use of non-zero framesPerBuffer for a callback + stream may introduce an additional layer of buffering which could introduce + additional latency. PortAudio guarantees that the additional latency + will be kept to the theoretical minimum however, it is strongly recommended + that a non-zero framesPerBuffer value only be used when your algorithm + requires a fixed number of frames per stream callback. - framesPerBuffer is the length in sample frames of all internal sample buffers - used for communication with platform specific audio routines. Wherever - possible this corresponds to the framesPerBuffer parameter passed to the - callback function. - - numberOfBuffers is the number of buffers used for multibuffered communication - with the platform specific audio routines. If you pass zero, then an optimum - value will be chosen for you internally. This parameter is provided only - as a guide - and does not imply that an implementation must use multibuffered - i/o when reliable double buffering is available (such as SndPlayDoubleBuffer() - on the Macintosh.) - - streamFlags may contain a combination of flags ORed together. - These flags modify the behaviour of the streaming process. Some flags may only - be relevant to certain buffer formats. - - callback is a pointer to a client supplied function that is responsible - for processing and filling input and output buffers (see above for details.) - - userData is a client supplied pointer which is passed to the callback + @param streamFlags Flags which modify the behaviour of the streaming process. + This parameter may contain a combination of flags ORed together. Some flags may + only be relevant to certain buffer formats. + + @param streamCallback A pointer to a client supplied function that is responsible + for processing and filling input and output buffers. If this parameter is NULL + the stream will be opened in 'blocking read/write' mode. In blocking mode, + the client can receive sample data using Pa_ReadStream and write sample data + using Pa_WriteStream, the number of samples that may be read or written + without blocking is returned by Pa_GetStreamReadAvailable and + Pa_GetStreamWriteAvailable respectively. + + @param userData A client supplied pointer which is passed to the stream callback function. It could for example, contain a pointer to instance data necessary for processing the audio buffers. - - return value: - Upon success Pa_OpenStream() returns PaNoError and places a pointer to a - valid PortAudioStream in the stream argument. The stream is inactive (stopped). - If a call to Pa_OpenStream() fails a non-zero error code is returned (see - PaError above) and the value of stream is invalid. - + + @return + Upon success Pa_OpenStream() returns paNoError and places a pointer to a + valid PaStream in the stream argument. The stream is inactive (stopped). + If a call to Pa_OpenStream() fails, a non-zero error code is returned (see + PaError for possible error codes) and the value of stream is invalid. + + @see PaStreamParameters, PaStreamCallback, Pa_ReadStream, Pa_WriteStream, + Pa_GetStreamReadAvailable, Pa_GetStreamWriteAvailable */ - -PaError Pa_OpenStream( PortAudioStream** stream, - PaDeviceID inputDevice, - int numInputChannels, - PaSampleFormat inputSampleFormat, - void *inputDriverInfo, - PaDeviceID outputDevice, - int numOutputChannels, - PaSampleFormat outputSampleFormat, - void *outputDriverInfo, +PaError Pa_OpenStream( PaStream** stream, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, double sampleRate, unsigned long framesPerBuffer, - unsigned long numberOfBuffers, PaStreamFlags streamFlags, - PortAudioCallback *callback, + PaStreamCallback *streamCallback, void *userData ); -/* - Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that opens - the default input and/or output devices. Most parameters have identical meaning - to their Pa_OpenStream() counterparts, with the following exceptions: +/** A simplified version of Pa_OpenStream() that opens the default input + and/or output devices. + + @param stream The address of a PaStream pointer which will receive + a pointer to the newly opened stream. - If either numInputChannels or numOutputChannels is 0 the respective device - is not opened. This has the same effect as passing paNoDevice in the device - arguments to Pa_OpenStream(). + @param numInputChannels The number of channels of sound that will be supplied + to the stream callback or returned by Pa_ReadStream. It can range from 1 to + the value of maxInputChannels in the PaDeviceInfo record for the default input + device. If 0 the stream is opened as an output-only stream. + + @param numOutputChannels The number of channels of sound to be delivered to the + stream callback or passed to Pa_WriteStream. It can range from 1 to the value + of maxOutputChannels in the PaDeviceInfo record for the default output dvice. + If 0 the stream is opened as an output-only stream. + + @param sampleFormat The sample format of both the input and output buffers + provided to the callback or passed to and from Pa_ReadStream and Pa_WriteStream. + sampleFormat may be any of the formats described by the PaSampleFormat enumeration + (see above). + FIXME: the following may need to be rewritten - PortAudio guarantees support for + the device's native formats (nativeSampleFormats in the device info record) + and additionally 16 and 32 bit integer and 32 bit float - sampleFormat applies to both the input and output buffers. + @param sampleRate Same as Pa_OpenStream parameter of the same name. + @param framesPerBuffer Same as Pa_OpenStream parameter of the same name. + @param streamCallback Same as Pa_OpenStream parameter of the same name. + @param userData Same as Pa_OpenStream parameter of the same name. -*/ + @return As for Pa_OpenStream -PaError Pa_OpenDefaultStream( PortAudioStream** stream, + @see Pa_OpenStream, PaStreamCallback +*/ +PaError Pa_OpenDefaultStream( PaStream** stream, int numInputChannels, int numOutputChannels, PaSampleFormat sampleFormat, double sampleRate, unsigned long framesPerBuffer, - unsigned long numberOfBuffers, - PortAudioCallback *callback, + PaStreamCallback *streamCallback, void *userData ); -/* - Pa_CloseStream() closes an audio stream, flushing any pending buffers. +/** Closes an audio stream. If the audio stream is active it + discards any pending buffers as if Pa_AbortStream() had been called. +*/ +PaError Pa_CloseStream( PaStream *stream ); + + +/** Functions of type PaStreamFinishedCallback are implemented by PortAudio + clients. They can be registered with a stream using the Pa_SetStreamFinishedCallback + function. Once registered they are called when the stream becomes inactive + (ie once a call to Pa_StopStream() will not block). + A stream will become inactive after the stream callback returns non-zero, + or when Pa_StopStream or Pa_AbortStream is called. For a stream providing audio + output, if the stream callback returns paComplete, or Pa_StopStream is called, + the stream finished callback will not be called until all generated sample data + has been played. + + @param userData The userData parameter supplied to Pa_OpenStream() + + @see Pa_SetStreamFinishedCallback */ +typedef void PaStreamFinishedCallback( void *userData ); -PaError Pa_CloseStream( PortAudioStream* ); -/* - Pa_StartStream() and Pa_StopStream() begin and terminate audio processing. - Pa_StopStream() waits until all pending audio buffers have been played. - Pa_AbortStream() stops playing immediately without waiting for pending +/** Register a stream finished callback function which will be called when the + stream becomes inactive. See the description of PaStreamFinishedCallback for + further details about when the callback will be called. + + @param stream a pointer to a PaStream that is in the stopped state - if the + stream is not stopped, the stream's finished callback will remain unchanged + and an error code will be returned. + + @param streamFinishedCallback a pointer to a function with the same signature + as PaStreamFinishedCallback, that will be called when the stream becomes + inactive. Passing NULL for this parameter will un-register a previously + registered stream finished callback function. + + @return on success returns paNoError, otherwise an error code indicating the cause + of the error. + + @see PaStreamFinishedCallback +*/ +PaError Pa_SetStreamFinishedCallback( PaStream *stream, PaStreamFinishedCallback* streamFinishedCallback ); + + +/** Commences audio processing. +*/ +PaError Pa_StartStream( PaStream *stream ); + + +/** Terminates audio processing. It waits until all pending + audio buffers have been played before it returns. +*/ +PaError Pa_StopStream( PaStream *stream ); + + +/** Terminates audio processing immediately without waiting for pending buffers to complete. - */ +PaError Pa_AbortStream( PaStream *stream ); -PaError Pa_StartStream( PortAudioStream *stream ); -PaError Pa_StopStream( PortAudioStream *stream ); +/** @return Returns one (1) when the stream is stopped, zero (0) when + the stream is running, or a negative error number if the stream + is invalid. A stream is considered to be stopped prior to a successful + call to Pa_StartStream and after a successful call to Pa_StopStream + or Pa_AbortStream. If a stream callback returns a value other than + paContinue the stream is NOT considered to be stopped. +*/ +PaError Pa_IsStreamStopped( PaStream *stream ); -PaError Pa_AbortStream( PortAudioStream *stream ); -/* - Pa_StreamActive() returns one (1) when the stream is active (ie playing +/** @return Returns one (1) when the stream is active (ie playing or recording audio), zero (0) when not playing, or a negative error number if the stream is invalid. - The stream is active between calls to Pa_StartStream() and Pa_StopStream(), - but may also become inactive if the callback returns a non-zero value. - In the latter case, the stream is considered inactive after the last - buffer has finished playing. - + + A stream is active after a successful call to Pa_StartStream(), until it + becomes inactive either as a result of a call to Pa_StopStream() or + Pa_AbortStream(), or as a result of a return value other than paContinue from + the stream callback. In the latter case, the stream is considered inactive after + the last buffer has finished playing. + + @see Pa_StopStream, Pa_AbortStream */ +PaError Pa_IsStreamActive( PaStream *stream ); -PaError Pa_StreamActive( PortAudioStream *stream ); -/* - Pa_StreamTime() returns the current output time in samples for the stream. - This time may be used as a time reference (for example synchronizing audio to - MIDI). - + +/** A structure containing unchanging information about an open stream. + @see Pa_GetStreamInfo */ -PaTimestamp Pa_StreamTime( PortAudioStream *stream ); +typedef struct PaStreamInfo +{ + /** this is struct version 1 */ + int structVersion; -/* - Pa_GetCPULoad() returns the CPU Load for the stream. - The "CPU Load" is a fraction of total CPU time consumed by the stream's + /** The input latency of the stream in seconds. This value provides the most + accurate estimate of input latency available to the implementation. It may + differ significantly from the suggestedLatency value passed to Pa_OpenStream(). + The value of this field will be zero (0.) for output-only streams. + @see PaTime + */ + PaTime inputLatency; + + /** The output latency of the stream in seconds. This value provides the most + accurate estimate of output latency available to the implementation. It may + differ significantly from the suggestedLatency value passed to Pa_OpenStream(). + The value of this field will be zero (0.) for input-only streams. + @see PaTime + */ + PaTime outputLatency; + + /** The sample rate of the stream in Hertz (samples per second). In cases + where the hardware sample rate is inaccurate and PortAudio is aware of it, + the value of this field may be different from the sampleRate parameter + passed to Pa_OpenStream(). If information about the actual hardware sample + rate is not available, this field will have the same value as the sampleRate + parameter passed to Pa_OpenStream(). + */ + double sampleRate; + +} PaStreamInfo; + + +/** Retrieve a pointer to a PaStreamInfo structure containing information + about the specified stream. + @return A pointer to an immutable PaStreamInfo structure. If the stream + parameter invalid, or an error is encountered, the function returns NULL. + + @param stream A pointer to an open stream previously created with Pa_OpenStream. + + @note PortAudio manages the memory referenced by the returned pointer, + the client must not manipulate or free the memory. The pointer is only + guaranteed to be valid until the specified stream is closed. + + @see PaStreamInfo +*/ +const PaStreamInfo* Pa_GetStreamInfo( PaStream *stream ); + + +/** + @return The current time (in seconds) according to the same clock used to + generate buffer timestamps for stream. + This time may be used for syncronising other events to the audio stream, + for example synchronizing audio to MIDI. + + @see PaTime, PaStreamCallback +*/ +PaTime Pa_GetStreamTime( PaStream *stream ); + + +/** Retrieve CPU usage information for the specified stream. + The "CPU Load" is a fraction of total CPU time consumed by a callback stream's audio processing routines including, but not limited to the client supplied - callback. - A value of 0.5 would imply that PortAudio and the sound generating - callback was consuming roughly 50% of the available CPU time. - This function may be called from the callback function or the application. - + stream callback. This function does not work with blocking read/write streams. + + This function may be called from the stream callback function or the + application. + + @return + A floating point value, typically between 0.0 and 1.0, where 1.0 indicates + that the stream callback is consuming the maximum number of CPU cycles possible + to maintain real-time operation. A value of 0.5 would imply that PortAudio and + the stream callback was consuming roughly 50% of the available CPU time. The + return value may exceed 1.0. A value of 0.0 will always be returned for a + blocking read/write stream. */ +double Pa_GetStreamCpuLoad( PaStream* stream ); -double Pa_GetCPULoad( PortAudioStream* stream ); -/* - Pa_GetMinNumBuffers() returns the minimum number of buffers required by - the current host based on minimum latency. - On the PC, for the DirectSound implementation, latency can be optionally set - by user by setting an environment variable. - For example, to set latency to 200 msec, put: - - set PA_MIN_LATENCY_MSEC=200 - - in the AUTOEXEC.BAT file and reboot. - If the environment variable is not set, then the latency will be determined - based on the OS. Windows NT has higher latency than Win95. - +/** Read samples from an input stream. The function doesn't return until + the entire buffer has been filled - this may involve waiting for the operating + system to supply the data. + + @param buffer A pointer to a buffer of sample frames. The buffer contains + samples in the format specified by the inputParameters->sampleFormat field + used to open the stream, and the number of channels specified by + inputParameters->numChannels. If non-interleaved samples were requested, + buffer is a pointer to the first element of an array of non-interleaved + buffer pointers, one for each channel. + + @param frames The number of frames to be read into buffer. This parameter + is not constrained to a specific range, however high performance applications + will want to match this parameter to the framesPerBuffer parameter used + when opening the stream. + + @return On success PaNoError will be returned, or PaInputOverflowed if input + data was discarded by PortAudio after the previous call and before this call. */ +PaError Pa_ReadStream( PaStream* stream, + void *buffer, + unsigned long frames ); + + +/** Write samples to an output stream. This function doesn't return until the + entire buffer has been consumed - this may involve waiting for the operating + system to consume the data. + + @param buffer A pointer to a buffer of sample frames. The buffer contains + samples in the format specified by the outputParameters->sampleFormat field + used to open the stream, and the number of channels specified by + outputParameters->numChannels. If non-interleaved samples were requested, + buffer is a pointer to the first element of an array of non-interleaved + buffer pointers, one for each channel. + + @param frames The number of frames to be written from buffer. This parameter + is not constrained to a specific range, however high performance applications + will want to match this parameter to the framesPerBuffer parameter used + when opening the stream. + + @return On success PaNoError will be returned, or paOutputUnderflowed if + additional output data was inserted after the previous call and before this + call. +*/ +PaError Pa_WriteStream( PaStream* stream, + void *buffer, + unsigned long frames ); -int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ); -/* - Pa_Sleep() puts the caller to sleep for at least 'msec' milliseconds. - You may sleep longer than the requested time so don't rely on this for - accurate musical timing. - - Pa_Sleep() is provided as a convenience for authors of portable code (such as - the tests and examples in the PortAudio distribution.) - +/** Retrieve the number of frames that can be read from the stream without + waiting. + + @return If non-negative, the return value is the maximum number of frames + that can be read from the stream without blocking or busy waiting. A + negative value is a PaErrorCode. */ +signed long Pa_GetStreamReadAvailable( PaStream* stream ); -void Pa_Sleep( long msec ); -/* - Pa_GetSampleSize() returns the size in bytes of a single sample in the - supplied PaSampleFormat, or paSampleFormatNotSupported if the format is - no supported. - +/** Retrieve the number of frames that can be written to the stream without + waiting. + + @return If non-negative, the return value is the maximum number of frames + that can be written to the stream without blocking or busy waiting. A + negative value is a PaErrorCode. */ +signed long Pa_GetStreamWriteAvailable( PaStream* stream ); + + +/* Miscellaneous utilities */ + +/** + @return The size in bytes of a single sample in the specified format, + or paSampleFormatNotSupported if the format is not supported. +*/ PaError Pa_GetSampleSize( PaSampleFormat format ); +/** Puts the caller to sleep for at least 'msec' milliseconds. + It may sleep longer than requested so don't rely on this for accurate + musical timing. + + This function is provided only as a convenience for authors of portable code + (such as the tests and examples in the PortAudio distribution.) +*/ +void Pa_Sleep( long msec ); + + + #ifdef __cplusplus } #endif /* __cplusplus */ -#endif /* PORT_AUDIO_H */ +#endif /* PORTAUDIO_H */ diff --git a/pd/portaudio/pa_dll_switch/PaDllEntry.h b/pd/portaudio/pa_dll_switch/PaDllEntry.h new file mode 100644 index 00000000..e070054b --- /dev/null +++ b/pd/portaudio/pa_dll_switch/PaDllEntry.h @@ -0,0 +1,184 @@ + +/* + * PortAudio Portable Real-Time Audio Library + * PortAudio DLL Header File + * Latest version available at: http://www.audiomulch.com/portaudio/ + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +// changed by zplane.developement in order to generate a DLL + +#ifndef __PADLLENTRY_HEADER_INCLUDED__ + +#define __PADLLENTRY_HEADER_INCLUDED__ + +typedef int PaError; +typedef enum { + paNoError = 0, + + paHostError = -10000, + paInvalidChannelCount, + paInvalidSampleRate, + paInvalidDeviceId, + paInvalidFlag, + paSampleFormatNotSupported, + paBadIODeviceCombination, + paInsufficientMemory, + paBufferTooBig, + paBufferTooSmall, + paNullCallback, + paBadStreamPtr, + paTimedOut, + paInternalError +} PaErrorNum; + +typedef unsigned long PaSampleFormat; +#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/ +#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/ +#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/ +#define paInt24 ((PaSampleFormat) (1<<3)) +#define paPackedInt24 ((PaSampleFormat) (1<<4)) +#define paInt8 ((PaSampleFormat) (1<<5)) +#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */ +#define paCustomFormat ((PaSampleFormat) (1<<16)) + + +typedef int PaDeviceID; +#define paNoDevice -1 + +typedef struct +{ + int structVersion; + const char *name; + int maxInputChannels; + int maxOutputChannels; + /* Number of discrete rates, or -1 if range supported. */ + int numSampleRates; + /* Array of supported sample rates, or {min,max} if range supported. */ + const double *sampleRates; + PaSampleFormat nativeSampleFormats; +} +PaDeviceInfo; + + +typedef double PaTimestamp; + + +typedef int (PortAudioCallback)( + void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); + + +#define paNoFlag (0) +#define paClipOff (1<<0) /* disable default clipping of out of range samples */ +#define paDitherOff (1<<1) /* disable default dithering */ +#define paPlatformSpecificFlags (0x00010000) +typedef unsigned long PaStreamFlags; + +typedef void PortAudioStream; +#define PaStream PortAudioStream + +extern PaError (__cdecl* Pa_Initialize)( void ); + + + +extern PaError (__cdecl* Pa_Terminate)( void ); + + +extern long (__cdecl* Pa_GetHostError)( void ); + + +extern const char* (__cdecl* Pa_GetErrorText)( PaError ); + + + +extern int (__cdecl* Pa_CountDevices)(void); + +extern PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void ); + +extern PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void ); + + +extern const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID); + + + +extern PaError (__cdecl* Pa_OpenStream)( + PortAudioStream ** , + PaDeviceID , + int , + PaSampleFormat , + void *, + PaDeviceID , + int , + PaSampleFormat , + void *, + double , + unsigned long , + unsigned long , + unsigned long , + PortAudioCallback *, + void * ); + + + +extern PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PortAudioCallback *callback, + void *userData ); + + +extern PaError (__cdecl* Pa_CloseStream)( PortAudioStream* ); + + +extern PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream ); + +extern PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream ); + +extern PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream ); + +extern PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream ); + +extern PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream ); + +extern double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream ); + +extern int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate ); + +extern void (__cdecl* Pa_Sleep)( long msec ); + +extern PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format ); + +#endif // __PADLLENTRY_HEADER_INCLUDED__ + diff --git a/pd/portaudio/pa_dll_switch/letter_from_tim_010817.txt b/pd/portaudio/pa_dll_switch/letter_from_tim_010817.txt new file mode 100644 index 00000000..a535cd1d Binary files /dev/null and b/pd/portaudio/pa_dll_switch/letter_from_tim_010817.txt differ diff --git a/pd/portaudio/pa_dll_switch/loadPA_DLL.cpp b/pd/portaudio/pa_dll_switch/loadPA_DLL.cpp new file mode 100644 index 00000000..043eda87 --- /dev/null +++ b/pd/portaudio/pa_dll_switch/loadPA_DLL.cpp @@ -0,0 +1,203 @@ +////////////////////////////////////////////////////////////////////////// + + +HINSTANCE pPaDll; + +/* + the function pointers to the PortAudio DLLs +*/ + +PaError (__cdecl* Pa_Initialize)( void ); + + + +PaError (__cdecl* Pa_Terminate)( void ); + + +long (__cdecl* Pa_GetHostError)( void ); + + +const char* (__cdecl* Pa_GetErrorText)( PaError ); + + +int (__cdecl* Pa_CountDevices)(void); + +PaDeviceID (__cdecl* Pa_GetDefaultInputDeviceID)( void ); + +PaDeviceID (__cdecl* Pa_GetDefaultOutputDeviceID)( void ); + + +const PaDeviceInfo* (__cdecl* Pa_GetDeviceInfo)( PaDeviceID); + + + +PaError (__cdecl* Pa_OpenStream)( + PortAudioStream ** , + PaDeviceID , + int , + PaSampleFormat , + void *, + PaDeviceID , + int , + PaSampleFormat , + void *, + double , + unsigned long , + unsigned long , + unsigned long , + PortAudioCallback *, + void * ); + + + +PaError (__cdecl* Pa_OpenDefaultStream)( PortAudioStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PortAudioCallback *callback, + void *userData ); + + +PaError (__cdecl* Pa_CloseStream)( PortAudioStream* ); + + +PaError (__cdecl* Pa_StartStream)( PortAudioStream *stream ); + +PaError (__cdecl* Pa_StopStream)( PortAudioStream *stream ); + +PaError (__cdecl* Pa_AbortStream)( PortAudioStream *stream ); + +PaError (__cdecl* Pa_StreamActive)( PortAudioStream *stream ); + +PaTimestamp (__cdecl* Pa_StreamTime)( PortAudioStream *stream ); + +double (__cdecl* Pa_GetCPULoad)( PortAudioStream* stream ); + +int (__cdecl* Pa_GetMinNumBuffers)( int framesPerBuffer, double sampleRate ); + +void (__cdecl* Pa_Sleep)( long msec ); + +PaError (__cdecl* Pa_GetSampleSize)( PaSampleFormat format ); + + +////////////////////////////////////////////////////////////////////////// + +... + +ZERROR AudioEngine::DirectXSupport(ZBOOL bSupDX) +{ + if (bSupDX) + if (CheckForDirectXSupport()) + bSupportDirectX = _TRUE; + else + return _NO_SOUND; + else + bSupportDirectX = _FALSE; + return _NO_ERROR; +} + + + +ZBOOL AudioEngine::CheckForDirectXSupport() +{ + HMODULE pTestDXLib; + FARPROC pFunctionality; + + pTestDXLib=LoadLibrary("DSOUND"); + if (pTestDXLib!=NULL) // check if there is a DirectSound + { + pFunctionality = GetProcAddress(pTestDXLib, (char*) 7); + if (pFunctionality!=NULL) + { + FreeLibrary(pTestDXLib); + return _TRUE; + } + else + { + FreeLibrary(pTestDXLib); + return _FALSE; + } + } + else + return _FALSE; +} + + +ZERROR AudioEngine::LoadPALib() +{ +#ifdef _DEBUG + if (bSupportDirectX) + pPaDll = LoadLibrary("PA_DXD"); + else + pPaDll = LoadLibrary("PA_MMED"); +#else + if (bSupportDirectX) + pPaDll = LoadLibrary("PA_DX"); + else + pPaDll = LoadLibrary("PA_MME"); +#endif + if (pPaDll!=NULL) + { + + Pa_Initialize = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Initialize"); + Pa_Terminate = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_Terminate"); + Pa_GetHostError = (long (__cdecl* )( void )) GetProcAddress(pPaDll,"Pa_GetHostError"); + Pa_GetErrorText = (const char* (__cdecl* )( PaError )) GetProcAddress(pPaDll,"Pa_GetErrorText"); + Pa_CountDevices = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_CountDevices"); + Pa_GetDefaultInputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultInputDeviceID"); + Pa_GetDefaultOutputDeviceID = (int (__cdecl*)(void))GetProcAddress(pPaDll,"Pa_GetDefaultOutputDeviceID"); + Pa_GetDeviceInfo = (const PaDeviceInfo* (__cdecl* )( PaDeviceID)) GetProcAddress(pPaDll,"Pa_GetDeviceInfo"); + Pa_OpenStream = ( PaError (__cdecl* )( + PortAudioStream ** , + PaDeviceID , + int , + PaSampleFormat , + void *, + PaDeviceID , + int , + PaSampleFormat , + void *, + double , + unsigned long , + unsigned long , + unsigned long , + PortAudioCallback *, + void * )) GetProcAddress(pPaDll,"Pa_OpenStream"); + + Pa_OpenDefaultStream = (PaError (__cdecl* )( PortAudioStream** , + int , + int , + PaSampleFormat , + double , + unsigned long , + unsigned long , + PortAudioCallback *, + void * )) GetProcAddress(pPaDll,"Pa_OpenDefaultStream"); + Pa_CloseStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_CloseStream"); + Pa_StartStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StartStream"); + Pa_StopStream = (PaError (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_StopStream"); + Pa_AbortStream = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_AbortStream"); + Pa_StreamActive = (PaError (__cdecl* )( PortAudioStream* )) GetProcAddress(pPaDll,"Pa_StreamActive"); + Pa_StreamTime = (PaTimestamp (__cdecl* )( PortAudioStream *))GetProcAddress(pPaDll,"Pa_StreamTime"); + Pa_GetCPULoad = (double (__cdecl* )( PortAudioStream* ))GetProcAddress(pPaDll,"Pa_GetCPULoad"); + Pa_GetMinNumBuffers = (int (__cdecl* )( int , double )) GetProcAddress(pPaDll,"Pa_GetMinNumBuffers"); + Pa_Sleep = (void (__cdecl* )( long )) GetProcAddress(pPaDll,"Pa_Sleep"); + Pa_GetSampleSize = (PaError (__cdecl* )( PaSampleFormat )) GetProcAddress(pPaDll,"Pa_GetSampleSize"); + + return _NO_ERROR; + } + else + return _DLL_NOT_FOUND; +} + +ZERROR AudioEngine::UnLoadPALib() +{ + if (pPaDll!=NULL) + FreeLibrary(pPaDll); + return _NO_ERROR; +} + +... diff --git a/pd/portaudio/pa_dll_switch/pa_lib.c b/pd/portaudio/pa_dll_switch/pa_lib.c new file mode 100644 index 00000000..86601592 --- /dev/null +++ b/pd/portaudio/pa_dll_switch/pa_lib.c @@ -0,0 +1,827 @@ +/* + * Portable Audio I/O Library + * Host Independant Layer + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2000 Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* Modification History: + PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC +*/ + +#include +#include +#include +#include + +/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ +#ifdef _WIN32 +#ifndef __MWERKS__ +#include +#endif /* __MWERKS__ */ +#else /* !_WIN32 */ +#include +#endif /* _WIN32 */ + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +/* The reason we might NOT want to validate the rate before opening the stream + * is because many DirectSound drivers lie about the rates they actually support. + */ +#define PA_VALIDATE_RATE (0) /* If true validate sample rate against driver info. */ + +/* +O- maybe not allocate past_InputBuffer and past_OutputBuffer if not needed for conversion +*/ + +#ifndef FALSE + #define FALSE (0) + #define TRUE (!FALSE) +#endif + +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +static int gInitCount = 0; /* Count number of times Pa_Initialize() called to allow nesting and overlapping. */ + +static PaError Pa_KillStream( PortAudioStream *stream, int abort ); + +/***********************************************************************/ +int PaHost_FindClosestTableEntry( double allowableError, const double *rateTable, int numRates, double frameRate ) +{ + double err, minErr = allowableError; + int i, bestFit = -1; + + for( i=0; inumSampleRates == -1 ) + { + /* Is it out of range? */ + if( (requestedFrameRate < pdi->sampleRates[0]) || + (requestedFrameRate > pdi->sampleRates[1]) ) + { + return paInvalidSampleRate; + } + + *closestFrameRatePtr = requestedFrameRate; + } + else + { + bestRateIndex = PaHost_FindClosestTableEntry( 1.0, pdi->sampleRates, pdi->numSampleRates, requestedFrameRate ); + if( bestRateIndex < 0 ) return paInvalidSampleRate; + *closestFrameRatePtr = pdi->sampleRates[bestRateIndex]; + } + return paNoError; +} + +/*************************************************************************/ +DLL_API PaError Pa_OpenStream( + PortAudioStream** streamPtrPtr, + PaDeviceID inputDeviceID, + int numInputChannels, + PaSampleFormat inputSampleFormat, + void *inputDriverInfo, + PaDeviceID outputDeviceID, + int numOutputChannels, + PaSampleFormat outputSampleFormat, + void *outputDriverInfo, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + unsigned long streamFlags, + PortAudioCallback *callback, + void *userData ) +{ + internalPortAudioStream *past = NULL; + PaError result = paNoError; + int bitsPerInputSample; + int bitsPerOutputSample; + /* Print passed parameters. */ + DBUG(("Pa_OpenStream( %p, %d, %d, %d, %p, /* input */ \n", + streamPtrPtr, inputDeviceID, numInputChannels, + inputSampleFormat, inputDriverInfo )); + DBUG((" %d, %d, %d, %p, /* output */\n", + outputDeviceID, numOutputChannels, + outputSampleFormat, outputDriverInfo )); + DBUG((" %g, %d, %d, 0x%x, , %p )\n", + sampleRate, framesPerBuffer, numberOfBuffers, + streamFlags, userData )); + + /* Check for parameter errors. */ + if( (streamFlags & ~(paClipOff | paDitherOff)) != 0 ) return paInvalidFlag; + if( streamPtrPtr == NULL ) return paBadStreamPtr; + if( inputDriverInfo != NULL ) return paHostError; /* REVIEW */ + if( outputDriverInfo != NULL ) return paHostError; /* REVIEW */ + if( (inputDeviceID < 0) && ( outputDeviceID < 0) ) return paInvalidDeviceId; + if( (outputDeviceID >= Pa_CountDevices()) || (inputDeviceID >= Pa_CountDevices()) ) return paInvalidDeviceId; + if( (numInputChannels <= 0) && ( numOutputChannels <= 0) ) return paInvalidChannelCount; + +#if SUPPORT_AUDIO_CAPTURE + if( inputDeviceID >= 0 ) + { + PaError size = Pa_GetSampleSize( inputSampleFormat ); + if( size < 0 ) return size; + bitsPerInputSample = 8 * size; + if( (numInputChannels <= 0) ) return paInvalidChannelCount; + } +#else + if( inputDeviceID >= 0 ) + { + return paInvalidChannelCount; + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + else + { + if( numInputChannels > 0 ) return paInvalidChannelCount; + bitsPerInputSample = 0; + } + + if( outputDeviceID >= 0 ) + { + PaError size = Pa_GetSampleSize( outputSampleFormat ); + if( size < 0 ) return size; + bitsPerOutputSample = 8 * size; + if( (numOutputChannels <= 0) ) return paInvalidChannelCount; + } + else + { + if( numOutputChannels > 0 ) return paInvalidChannelCount; + bitsPerOutputSample = 0; + } + + if( callback == NULL ) return paNullCallback; + + /* Allocate and clear stream structure. */ + past = (internalPortAudioStream *) PaHost_AllocateFastMemory( sizeof(internalPortAudioStream) ); + if( past == NULL ) return paInsufficientMemory; + memset( past, 0, sizeof(internalPortAudioStream) ); + AddTraceMessage("Pa_OpenStream: past", (long) past ); + + past->past_Magic = PA_MAGIC; /* Set ID to catch bugs. */ + past->past_FramesPerUserBuffer = framesPerBuffer; + past->past_NumUserBuffers = numberOfBuffers; /* NOTE - PaHost_OpenStream() NMUST CHECK FOR ZERO! */ + past->past_Callback = callback; + past->past_UserData = userData; + past->past_OutputSampleFormat = outputSampleFormat; + past->past_InputSampleFormat = inputSampleFormat; + past->past_OutputDeviceID = outputDeviceID; + past->past_InputDeviceID = inputDeviceID; + past->past_NumInputChannels = numInputChannels; + past->past_NumOutputChannels = numOutputChannels; + past->past_Flags = streamFlags; + + /* Check for absurd sample rates. */ + if( (sampleRate < 1000.0) || (sampleRate > 200000.0) ) + { + result = paInvalidSampleRate; + goto cleanup; + } + + /* Allocate buffers that may be used for format conversion from user to native buffers. */ + if( numInputChannels > 0 ) + { + +#if PA_VALIDATE_RATE + result = PaHost_ValidateSampleRate( inputDeviceID, sampleRate, &past->past_SampleRate ); + if( result < 0 ) + { + goto cleanup; + } +#else + past->past_SampleRate = sampleRate; +#endif + /* Allocate single Input buffer. */ + past->past_InputBufferSize = framesPerBuffer * numInputChannels * ((bitsPerInputSample+7) / 8); + past->past_InputBuffer = PaHost_AllocateFastMemory(past->past_InputBufferSize); + if( past->past_InputBuffer == NULL ) + { + result = paInsufficientMemory; + goto cleanup; + } + } + else + { + past->past_InputBuffer = NULL; + } + + /* Allocate single Output buffer. */ + if( numOutputChannels > 0 ) + { +#if PA_VALIDATE_RATE + result = PaHost_ValidateSampleRate( outputDeviceID, sampleRate, &past->past_SampleRate ); + if( result < 0 ) + { + goto cleanup; + } +#else + past->past_SampleRate = sampleRate; +#endif + past->past_OutputBufferSize = framesPerBuffer * numOutputChannels * ((bitsPerOutputSample+7) / 8); + past->past_OutputBuffer = PaHost_AllocateFastMemory(past->past_OutputBufferSize); + if( past->past_OutputBuffer == NULL ) + { + result = paInsufficientMemory; + goto cleanup; + } + } + else + { + past->past_OutputBuffer = NULL; + } + + result = PaHost_OpenStream( past ); + if( result < 0 ) goto cleanup; + + *streamPtrPtr = (void *) past; + + return result; + +cleanup: + if( past != NULL ) Pa_CloseStream( past ); + *streamPtrPtr = NULL; + return result; +} + + +/*************************************************************************/ +DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PortAudioCallback *callback, + void *userData ) +{ + return Pa_OpenStream( + stream, + ((numInputChannels > 0) ? Pa_GetDefaultInputDeviceID() : paNoDevice), + numInputChannels, sampleFormat, NULL, + ((numOutputChannels > 0) ? Pa_GetDefaultOutputDeviceID() : paNoDevice), + numOutputChannels, sampleFormat, NULL, + sampleRate, framesPerBuffer, numberOfBuffers, paNoFlag, callback, userData ); +} + +/*************************************************************************/ +DLL_API PaError Pa_CloseStream( PortAudioStream* stream) +{ + PaError result; + internalPortAudioStream *past; + + DBUG(("Pa_CloseStream()\n")); + if( stream == NULL ) return paBadStreamPtr; + past = (internalPortAudioStream *) stream; + + Pa_AbortStream( past ); + result = PaHost_CloseStream( past ); + + if( past->past_InputBuffer ) PaHost_FreeFastMemory( past->past_InputBuffer, past->past_InputBufferSize ); + if( past->past_OutputBuffer ) PaHost_FreeFastMemory( past->past_OutputBuffer, past->past_OutputBufferSize ); + PaHost_FreeFastMemory( past, sizeof(internalPortAudioStream) ); + + return result; +} + +/*************************************************************************/ +DLL_API PaError Pa_StartStream( PortAudioStream *stream ) +{ + PaError result = paHostError; + internalPortAudioStream *past; + + if( stream == NULL ) return paBadStreamPtr; + past = (internalPortAudioStream *) stream; + + past->past_FrameCount = 0.0; + + if( past->past_NumInputChannels > 0 ) + { + result = PaHost_StartInput( past ); + DBUG(("Pa_StartStream: PaHost_StartInput returned = 0x%X.\n", result)); + if( result < 0 ) goto error; + } + + if( past->past_NumOutputChannels > 0 ) + { + result = PaHost_StartOutput( past ); + DBUG(("Pa_StartStream: PaHost_StartOutput returned = 0x%X.\n", result)); + if( result < 0 ) goto error; + } + + result = PaHost_StartEngine( past ); + DBUG(("Pa_StartStream: PaHost_StartEngine returned = 0x%X.\n", result)); + if( result < 0 ) goto error; + + return paNoError; + +error: + return result; +} + +/*************************************************************************/ +DLL_API PaError Pa_StopStream( PortAudioStream *stream ) +{ + return Pa_KillStream( stream, 0 ); +} + +/*************************************************************************/ +DLL_API PaError Pa_AbortStream( PortAudioStream *stream ) +{ + return Pa_KillStream( stream, 1 ); +} + +/*************************************************************************/ +static PaError Pa_KillStream( PortAudioStream *stream, int abort ) +{ + PaError result = paNoError; + internalPortAudioStream *past; + + DBUG(("Pa_StopStream().\n")); + if( stream == NULL ) return paBadStreamPtr; + past = (internalPortAudioStream *) stream; + + if( (past->past_NumInputChannels > 0) || (past->past_NumOutputChannels > 0) ) + { + result = PaHost_StopEngine( past, abort ); + DBUG(("Pa_StopStream: PaHost_StopEngine returned = 0x%X.\n", result)); + if( result < 0 ) goto error; + } + + if( past->past_NumInputChannels > 0 ) + { + result = PaHost_StopInput( past, abort ); + DBUG(("Pa_StopStream: PaHost_StopInput returned = 0x%X.\n", result)); + if( result != paNoError ) goto error; + } + + if( past->past_NumOutputChannels > 0 ) + { + result = PaHost_StopOutput( past, abort ); + DBUG(("Pa_StopStream: PaHost_StopOutput returned = 0x%X.\n", result)); + if( result != paNoError ) goto error; + } + +error: + past->past_Usage = 0; + past->past_IfLastExitValid = 0; + + return result; +} + +/*************************************************************************/ +DLL_API PaError Pa_StreamActive( PortAudioStream *stream ) +{ + internalPortAudioStream *past; + if( stream == NULL ) return paBadStreamPtr; + past = (internalPortAudioStream *) stream; + return PaHost_StreamActive( past ); +} + +/*************************************************************************/ +DLL_API const char *Pa_GetErrorText( PaError errnum ) +{ + const char *msg; + + switch(errnum) + { + case paNoError: msg = "Success"; break; + case paHostError: msg = "Host error."; break; + case paInvalidChannelCount: msg = "Invalid number of channels."; break; + case paInvalidSampleRate: msg = "Invalid sample rate."; break; + case paInvalidDeviceId: msg = "Invalid device ID."; break; + case paInvalidFlag: msg = "Invalid flag."; break; + case paSampleFormatNotSupported: msg = "Sample format not supported"; break; + case paBadIODeviceCombination: msg = "Illegal combination of I/O devices."; break; + case paInsufficientMemory: msg = "Insufficient memory."; break; + case paBufferTooBig: msg = "Buffer too big."; break; + case paBufferTooSmall: msg = "Buffer too small."; break; + case paNullCallback: msg = "No callback routine specified."; break; + case paBadStreamPtr: msg = "Invalid stream pointer."; break; + case paTimedOut : msg = "Wait Timed Out."; break; + case paInternalError: msg = "Internal PortAudio Error."; break; + default: msg = "Illegal error number."; break; + } + return msg; +} + +/* + Get CPU Load as a fraction of total CPU time. + A value of 0.5 would imply that PortAudio and the sound generating + callback was consuming roughly 50% of the available CPU time. + The amount may vary depending on CPU load. + This function may be called from the callback function. +*/ +DLL_API double Pa_GetCPULoad( PortAudioStream* stream) +{ + internalPortAudioStream *past; + if( stream == NULL ) return (double) paBadStreamPtr; + past = (internalPortAudioStream *) stream; + return past->past_Usage; +} + +/************************************************************* +** Calculate 2 LSB dither signal with a triangular distribution. +** Ranged properly for adding to a 32 bit integer prior to >>15. +*/ +#define DITHER_BITS (15) +#define DITHER_SCALE (1.0f / ((1<>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS)); + /* High pass filter to reduce audibility. */ + highPass = current - previous; + previous = current; + return highPass; +} + +/************************************************************************* +** Called by host code. +** Convert input from Int16, call user code, then convert output +** to Int16 format for native use. +** Assumes host native format is paInt16. +** Returns result from user callback. +*/ +long Pa_CallConvertInt16( internalPortAudioStream *past, + short *nativeInputBuffer, + short *nativeOutputBuffer ) +{ + long temp; + long bytesEmpty = 0; + long bytesFilled = 0; + int userResult; + unsigned int i; + void *inputBuffer = NULL; + void *outputBuffer = NULL; + +#if SUPPORT_AUDIO_CAPTURE + /* Get native data from DirectSound. */ + if( (past->past_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) + { + /* Convert from native format to PA format. */ + unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumInputChannels; + switch(past->past_InputSampleFormat) + { + + case paFloat32: + { + float *inBufPtr = (float *) past->past_InputBuffer; + inputBuffer = past->past_InputBuffer; + for( i=0; ipast_InputBuffer; + inputBuffer = past->past_InputBuffer; + for( i=0; ipast_InputBuffer; + inputBuffer = past->past_InputBuffer; + if( past->past_Flags & paDitherOff ) + { + for( i=0; i> 8); + } + } + else + { + for( i=0; i> 7; + temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); + inBufPtr[i] = (char)(temp >> 8); + } + } + break; + } + + case paUInt8: + { + /* Convert 16 bit data to 8 bit unsigned chars */ + unsigned char *inBufPtr = (unsigned char *) past->past_InputBuffer; + inputBuffer = past->past_InputBuffer; + if( past->past_Flags & paDitherOff ) + { + for( i=0; i> 8)) + 0x80; + } + } + else + { + /* If you dither then you have to clip because dithering could push the signal out of range! */ + for( i=0; i> 7; + temp = ((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); + inBufPtr[i] = (unsigned char)(temp + 0x80); + } + } + break; + } + + default: + break; + } + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + + /* Are we doing output time? */ + if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) + { + /* May already be in native format so just write directly to native buffer. */ + outputBuffer = (past->past_OutputSampleFormat == paInt16) ? + nativeOutputBuffer : past->past_OutputBuffer; + } + /* + AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer ); + AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer ); + */ + /* Call user callback routine. */ + userResult = past->past_Callback( + inputBuffer, + outputBuffer, + past->past_FramesPerUserBuffer, + past->past_FrameCount, + past->past_UserData ); + + past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer; + + /* Convert to native format if necessary. */ + if( outputBuffer != NULL ) + { + unsigned int samplesPerBuffer = past->past_FramesPerUserBuffer * past->past_NumOutputChannels; + switch(past->past_OutputSampleFormat) + { + case paFloat32: + { + float *outBufPtr = (float *) past->past_OutputBuffer; + if( past->past_Flags & paDitherOff ) + { + if( past->past_Flags & paClipOff ) /* NOTHING */ + { + for( i=0; i 0x7FFF) ? 0x7FFF : temp)); + } + } + } + else + { + /* If you dither then you have to clip because dithering could push the signal out of range! */ + for( i=0; i 0x7FFF) ? 0x7FFF : temp)); + } + } + break; + } + + case paInt32: + { + int *outBufPtr = (int *) past->past_OutputBuffer; + if( past->past_Flags & paDitherOff ) + { + for( i=0; i> 16 ); + } + } + else + { + for( i=0; i> 1) + Pa_TriangularDither(); + temp = temp >> 15; + *nativeOutputBuffer++ = (short)((temp < -0x8000) ? -0x8000 : ((temp > 0x7FFF) ? 0x7FFF : temp)); + } + } + break; + } + + case paInt8: + { + char *outBufPtr = (char *) past->past_OutputBuffer; + for( i=0; ipast_OutputBuffer; + for( i=0; ipast_NumInputChannels > 0) && (nativeInputBuffer != NULL) ) + { + inputBuffer = nativeInputBuffer; // FIXME + } + + /* Are we doing output time? */ + if( (past->past_NumOutputChannels > 0) && (nativeOutputBuffer != NULL) ) + { + /* May already be in native format so just write directly to native buffer. */ + outputBuffer = (past->past_OutputSampleFormat == paFloat32) ? + nativeOutputBuffer : past->past_OutputBuffer; + } + /* + AddTraceMessage("Pa_CallConvertInt16: inputBuffer = ", (int) inputBuffer ); + AddTraceMessage("Pa_CallConvertInt16: outputBuffer = ", (int) outputBuffer ); + */ + /* Call user callback routine. */ + userResult = past->past_Callback( + inputBuffer, + outputBuffer, + past->past_FramesPerUserBuffer, + past->past_FrameCount, + past->past_UserData ); + + past->past_FrameCount += (PaTimestamp) past->past_FramesPerUserBuffer; + + /* Convert to native format if necessary. */ // FIXME + return userResult; +} + +/*************************************************************************/ +DLL_API PaError Pa_Initialize( void ) +{ + if( gInitCount++ > 0 ) return paNoError; + ResetTraceMessages(); + return PaHost_Init(); +} + +DLL_API PaError Pa_Terminate( void ) +{ + PaError result = paNoError; + + if( gInitCount == 0 ) return paNoError; + else if( --gInitCount == 0 ) + { + result = PaHost_Term(); + DumpTraceMessages(); + } + return result; +} + +/*************************************************************************/ +DLL_API PaError Pa_GetSampleSize( PaSampleFormat format ) +{ + int size; + switch(format ) + { + + case paUInt8: + case paInt8: + size = 1; + break; + + case paInt16: + size = 2; + break; + + case paPackedInt24: + size = 3; + break; + + case paFloat32: + case paInt32: + case paInt24: + size = 4; + break; + + default: + size = paSampleFormatNotSupported; + break; + } + return (PaError) size; +} + + diff --git a/pd/portaudio/pa_dll_switch/portaudio.h b/pd/portaudio/pa_dll_switch/portaudio.h new file mode 100644 index 00000000..9632521e --- /dev/null +++ b/pd/portaudio/pa_dll_switch/portaudio.h @@ -0,0 +1,439 @@ +#ifndef PORT_AUDIO_H +#define PORT_AUDIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * PortAudio Portable Real-Time Audio Library + * PortAudio API Header File + * Latest version available at: http://www.audiomulch.com/portaudio/ + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +// added by zplane.developement in order to generate a DLL + +#if defined(PA_MME_EXPORTS) || defined(PA_DX_EXPORTS) +#define DLL_API __declspec( dllexport ) +#elif defined(_LIB) || defined(_STATIC_LINK) || defined(_STATIC_APP) +#define DLL_API +#else +#define DLL_API __declspec(dllexport) +#endif + + +typedef int PaError; +typedef enum { + paNoError = 0, + + paHostError = -10000, + paInvalidChannelCount, + paInvalidSampleRate, + paInvalidDeviceId, + paInvalidFlag, + paSampleFormatNotSupported, + paBadIODeviceCombination, + paInsufficientMemory, + paBufferTooBig, + paBufferTooSmall, + paNullCallback, + paBadStreamPtr, + paTimedOut, + paInternalError +} PaErrorNum; + +/* + Pa_Initialize() is the library initialisation function - call this before + using the library. +*/ + +DLL_API PaError Pa_Initialize( void ); + +/* + Pa_Terminate() is the library termination function - call this after + using the library. +*/ + +DLL_API PaError Pa_Terminate( void ); + +/* + Return host specific error. + This can be called after receiving a paHostError. +*/ +DLL_API long Pa_GetHostError( void ); + +/* + Translate the error number into a human readable message. +*/ +DLL_API const char *Pa_GetErrorText( PaError errnum ); + +/* + Sample formats + + These are formats used to pass sound data between the callback and the + stream. Each device has a "native" format which may be used when optimum + efficiency or control over conversion is required. + + Formats marked "always available" are supported (emulated) by all devices. + + The floating point representation uses +1.0 and -1.0 as the respective + maximum and minimum. + +*/ + +typedef unsigned long PaSampleFormat; +#define paFloat32 ((PaSampleFormat) (1<<0)) /*always available*/ +#define paInt16 ((PaSampleFormat) (1<<1)) /*always available*/ +#define paInt32 ((PaSampleFormat) (1<<2)) /*always available*/ +#define paInt24 ((PaSampleFormat) (1<<3)) +#define paPackedInt24 ((PaSampleFormat) (1<<4)) +#define paInt8 ((PaSampleFormat) (1<<5)) +#define paUInt8 ((PaSampleFormat) (1<<6)) /* unsigned 8 bit, 128 is "ground" */ +#define paCustomFormat ((PaSampleFormat) (1<<16)) + +/* + Device enumeration mechanism. + + Device ids range from 0 to Pa_CountDevices()-1. + + Devices may support input, output or both. Device 0 is always the "default" + device and should support at least stereo in and out if that is available + on the taget platform _even_ if this involves kludging an input/output + device on platforms that usually separate input from output. Other platform + specific devices are specified by positive device ids. +*/ + +typedef int PaDeviceID; +#define paNoDevice -1 + +typedef struct +{ + int structVersion; + const char *name; + int maxInputChannels; + int maxOutputChannels; + /* Number of discrete rates, or -1 if range supported. */ + int numSampleRates; + /* Array of supported sample rates, or {min,max} if range supported. */ + const double *sampleRates; + PaSampleFormat nativeSampleFormats; +} +PaDeviceInfo; + + +DLL_API int Pa_CountDevices(); +/* + Pa_GetDefaultInputDeviceID(), Pa_GetDefaultOutputDeviceID() + + Return the default device ID or paNoDevice if there is no devices. + The result can be passed to Pa_OpenStream(). + + On the PC, the user can specify a default device by + setting an environment variable. For example, to use device #1. + + set PA_RECOMMENDED_OUTPUT_DEVICE=1 + + The user should first determine the available device ID by using + the supplied application "pa_devs". +*/ +DLL_API PaDeviceID Pa_GetDefaultInputDeviceID( void ); +DLL_API PaDeviceID Pa_GetDefaultOutputDeviceID( void ); + +/* + PaTimestamp is used to represent a continuous sample clock with arbitrary + start time useful for syncronisation. The type is used in the outTime + argument to the callback function and the result of Pa_StreamTime() +*/ + +typedef double PaTimestamp; + +/* + Pa_GetDeviceInfo() returns a pointer to an immutable PaDeviceInfo structure + referring to the device specified by id. + If id is out of range the function returns NULL. + + The returned structure is owned by the PortAudio implementation and must + not be manipulated or freed. The pointer is guaranteed to be valid until + between calls to Pa_Initialize() and Pa_Terminate(). +*/ + +DLL_API const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ); + +/* + PortAudioCallback is implemented by clients of the portable audio api. + + inputBuffer and outputBuffer are arrays of interleaved samples, + the format, packing and number of channels used by the buffers are + determined by parameters to Pa_OpenStream() (see below). + + framesPerBuffer is the number of sample frames to be processed by the callback. + + outTime is the time in samples when the buffer(s) processed by + this callback will begin being played at the audio output. + See also Pa_StreamTime() + + userData is the value of a user supplied pointer passed to Pa_OpenStream() + intended for storing synthesis data etc. + + return value: + The callback can return a nonzero value to stop the stream. This may be + useful in applications such as soundfile players where a specific duration + of output is required. However, it is not necessary to utilise this mechanism + as StopStream() will also terminate the stream. A callback returning a + nonzero value must fill the entire outputBuffer. + + NOTE: None of the other stream functions may be called from within the + callback function except for Pa_GetCPULoad(). + +*/ + +typedef int (PortAudioCallback)( + void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); + + +/* + Stream flags + + These flags may be supplied (ored together) in the streamFlags argument to + the Pa_OpenStream() function. + + [ suggestions? ] +*/ + +#define paNoFlag (0) +#define paClipOff (1<<0) /* disable defult clipping of out of range samples */ +#define paDitherOff (1<<1) /* disable default dithering */ +#define paPlatformSpecificFlags (0x00010000) +typedef unsigned long PaStreamFlags; + +/* + A single PortAudioStream provides multiple channels of real-time + input and output audio streaming to a client application. + Pointers to PortAudioStream objects are passed between PortAudio functions. +*/ + +typedef void PortAudioStream; +#define PaStream PortAudioStream + +/* + Pa_OpenStream() opens a stream for either input, output or both. + + stream is the address of a PortAudioStream pointer which will receive + a pointer to the newly opened stream. + + inputDevice is the id of the device used for input (see PaDeviceID above.) + inputDevice may be paNoDevice to indicate that an input device is not required. + + numInputChannels is the number of channels of sound to be delivered to the + callback. It can range from 1 to the value of maxInputChannels in the + device input record for the device specified in the inputDevice parameter. + If inputDevice is paNoDevice numInputChannels is ignored. + + inputSampleFormat is the format of inputBuffer provided to the callback + function. inputSampleFormat may be any of the formats described by the + PaSampleFormat enumeration (see above). PortAudio guarantees support for + the sound devices native formats (nativeSampleFormats in the device info + record) and additionally 16 and 32 bit integer and 32 bit floating point + formats. Support for other formats is implementation defined. + + inputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or stream processing. + inputDriverInfo is never required for correct operation. If not used + inputDriverInfo should be NULL. + + outputDevice is the id of the device used for output (see PaDeviceID above.) + outputDevice may be paNoDevice to indicate that an output device is not required. + + numOutputChannels is the number of channels of sound to be supplied by the + callback. See the definition of numInputChannels above for more details. + + outputSampleFormat is the sample format of the outputBuffer filled by the + callback function. See the definition of inputSampleFormat above for more + details. + + outputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or stream processing. + outputDriverInfo is never required for correct operation. If not used + outputDriverInfo should be NULL. + + sampleRate is the desired sampleRate for input and output + + framesPerBuffer is the length in sample frames of all internal sample buffers + used for communication with platform specific audio routines. Wherever + possible this corresponds to the framesPerBuffer parameter passed to the + callback function. + + numberOfBuffers is the number of buffers used for multibuffered + communication with the platform specific audio routines. This parameter is + provided only as a guide - and does not imply that an implementation must + use multibuffered i/o when reliable double buffering is available (such as + SndPlayDoubleBuffer() on the Macintosh.) + + streamFlags may contain a combination of flags ORed together. + These flags modify the behavior of the + streaming process. Some flags may only be relevant to certain buffer formats. + + callback is a pointer to a client supplied function that is responsible + for processing and filling input and output buffers (see above for details.) + + userData is a client supplied pointer which is passed to the callback + function. It could for example, contain a pointer to instance data necessary + for processing the audio buffers. + + return value: + Apon success Pa_OpenStream() returns PaNoError and places a pointer to a + valid PortAudioStream in the stream argument. The stream is inactive (stopped). + If a call to Pa_OpenStream() fails a nonzero error code is returned (see + PAError above) and the value of stream is invalid. + +*/ + +DLL_API PaError Pa_OpenStream( PortAudioStream** stream, + PaDeviceID inputDevice, + int numInputChannels, + PaSampleFormat inputSampleFormat, + void *inputDriverInfo, + PaDeviceID outputDevice, + int numOutputChannels, + PaSampleFormat outputSampleFormat, + void *outputDriverInfo, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PaStreamFlags streamFlags, + PortAudioCallback *callback, + void *userData ); + + +/* + Pa_OpenDefaultStream() is a simplified version of Pa_OpenStream() that + opens the default input and/or ouput devices. Most parameters have + identical meaning to their Pa_OpenStream() counterparts, with the following + exceptions: + + If either numInputChannels or numOutputChannels is 0 the respective device + is not opened (same as passing paNoDevice in the device arguments to Pa_OpenStream() ) + + sampleFormat applies to both the input and output buffers. +*/ + +DLL_API PaError Pa_OpenDefaultStream( PortAudioStream** stream, + int numInputChannels, + int numOutputChannels, + PaSampleFormat sampleFormat, + double sampleRate, + unsigned long framesPerBuffer, + unsigned long numberOfBuffers, + PortAudioCallback *callback, + void *userData ); + +/* + Pa_CloseStream() closes an audio stream, flushing any pending buffers. +*/ + +DLL_API PaError Pa_CloseStream( PortAudioStream* ); + +/* + Pa_StartStream() and Pa_StopStream() begin and terminate audio processing. + When Pa_StopStream() returns, all pending audio buffers have been played. + Pa_AbortStream() stops playing immediately without waiting for pending + buffers to complete. +*/ + +DLL_API PaError Pa_StartStream( PortAudioStream *stream ); + +DLL_API PaError Pa_StopStream( PortAudioStream *stream ); + +DLL_API PaError Pa_AbortStream( PortAudioStream *stream ); + +/* + Pa_StreamActive() returns one when the stream is playing audio, + zero when not playing, or a negative error number if the + stream is invalid. + The stream is active between calls to Pa_StartStream() and Pa_StopStream(), + but may also become inactive if the callback returns a non-zero value. + In the latter case, the stream is considered inactive after the last + buffer has finished playing. +*/ + +DLL_API PaError Pa_StreamActive( PortAudioStream *stream ); + +/* + Pa_StreamTime() returns the current output time for the stream in samples. + This time may be used as a time reference (for example syncronising audio to + MIDI). +*/ + +DLL_API PaTimestamp Pa_StreamTime( PortAudioStream *stream ); + +/* + The "CPU Load" is a fraction of total CPU time consumed by the + stream's audio processing. + A value of 0.5 would imply that PortAudio and the sound generating + callback was consuming roughly 50% of the available CPU time. + This function may be called from the callback function or the application. +*/ +DLL_API double Pa_GetCPULoad( PortAudioStream* stream ); + +/* + Use Pa_GetMinNumBuffers() to determine minimum number of buffers required for + the current host based on minimum latency. + On the PC, for the DirectSound implementation, latency can be optionally set + by user by setting an environment variable. + For example, to set latency to 200 msec, put: + + set PA_MIN_LATENCY_MSEC=200 + + in the AUTOEXEC.BAT file and reboot. + If the environment variable is not set, then the latency will be determined + based on the OS. Windows NT has higher latency than Win95. +*/ + +DLL_API int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ); + +/* + Sleep for at least 'msec' milliseconds. + You may sleep longer than the requested time so don't rely + on this for accurate musical timing. +*/ +DLL_API void Pa_Sleep( long msec ); + +/* + Return size in bytes of a single sample in a given PaSampleFormat + or paSampleFormatNotSupported. +*/ +DLL_API PaError Pa_GetSampleSize( PaSampleFormat format ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORT_AUDIO_H */ diff --git a/pd/portaudio/pa_jack/pa_jack.c b/pd/portaudio/pa_jack/pa_jack.c new file mode 100644 index 00000000..4efeff81 --- /dev/null +++ b/pd/portaudio/pa_jack/pa_jack.c @@ -0,0 +1,864 @@ +/* + * $Id: pa_jack.c,v 1.1.2.1 2002/07/31 04:22:56 joshua Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * JACK Implementation by Joshua Haberman + * + * Copyright (c) 2002 Joshua Haberman + * + * 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. + */ + +#include +#include +#include +#include + +#include +#include + +#include "pa_util.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_process.h" +#include "pa_allocation.h" +#include "pa_cpuload.h" + +PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, + PaHostApiIndex hostApiIndex ); + +/* + * Functions that directly map to the PortAudio stream interface + */ + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + PaDeviceIndex inputDevice, + int numInputChannels, + PaSampleFormat inputSampleFormat, + unsigned long inputLatency, + PaHostApiSpecificStreamInfo *inputStreamInfo, + PaDeviceIndex outputDevice, + int numOutputChannels, + PaSampleFormat outputSampleFormat, + unsigned long outputLatency, + PaHostApiSpecificStreamInfo *outputStreamInfo, + double sampleRate, + unsigned long framesPerCallback, + PaStreamFlags streamFlags, + PortAudioCallback *callback, + void *userData ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTimestamp GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); + +/* + * Data specific to this API + */ + +typedef struct +{ + PaUtilHostApiRepresentation commonHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + + PaUtilAllocationGroup *deviceInfoMemory; + + jack_client_t *jack_client; + PaHostApiIndex hostApiIndex; +} +PaJackHostApiRepresentation; + +#define MAX_CLIENTS 100 +#define TRUE 1 +#define FALSE 0 + +/* + * Functions specific to this API + */ + +static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ); +static int JackCallback( jack_nframes_t frames, void *userData ); + + + +/* + * + * Implementation + * + */ + + +/* BuildDeviceList(): + * + * The process of determining a list of PortAudio "devices" from + * JACK's client/port system is fairly involved, so it is separated + * into its own routine. + */ + +static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi ) +{ + /* Utility macros for the repetitive process of allocating memory */ + + /* ... MALLOC: allocate memory as part of the device list + * allocation group */ +#define MALLOC(size) \ + (PaUtil_GroupAllocateMemory( jackApi->deviceInfoMemory, (size) )) + + /* ... MEMVERIFY: make sure we didn't get NULL */ +#define MEMVERIFY(ptr) \ + if( (ptr) == NULL ) return paInsufficientMemory; + + /* JACK has no concept of a device. To JACK, there are clients + * which have an arbitrary number of ports. To make this + * intelligible to PortAudio clients, we will group each JACK client + * into a device, and make each port of that client a channel */ + + PaUtilHostApiRepresentation *commonApi = &jackApi->commonHostApiRep; + + const char **jack_ports; + char *client_names[MAX_CLIENTS]; + int num_clients = 0; + int port_index, client_index, i; + double *globalSampleRate; + regex_t port_regex; + + /* since we are rebuilding the list of devices, free all memory + * associated with the previous list */ + PaUtil_FreeAllAllocations( jackApi->deviceInfoMemory ); + + /* We can only retrieve the list of clients indirectly, by first + * asking for a list of all ports, then parsing the port names + * according to the client_name:port_name convention (which is + * enforced by jackd) */ + jack_ports = jack_get_ports( jackApi->jack_client, "", "", 0 ); + + if( jack_ports == NULL ) + return paHostError; + + /* Parse the list of ports, using a regex to grab the client names */ + regcomp( &port_regex, "^[^:]*", REG_EXTENDED ); + + /* Build a list of clients from the list of ports */ + for( port_index = 0; jack_ports[port_index] != NULL; port_index++ ) + { + int client_seen; + regmatch_t match_info; + char tmp_client_name[100]; + + /* extract the client name from the port name, using a regex + * that parses the clientname:portname syntax */ + regexec( &port_regex, jack_ports[port_index], 1, &match_info, 0 ); + memcpy( tmp_client_name, &jack_ports[port_index][match_info.rm_so], + match_info.rm_eo - match_info.rm_so ); + tmp_client_name[ match_info.rm_eo - match_info.rm_so ] = '\0'; + + /* do we know about this port's client yet? */ + client_seen = FALSE; + + for( i = 0; i < num_clients; i++ ) + if( strcmp( tmp_client_name, client_names[i] ) == 0 ) + client_seen = TRUE; + + if( client_seen == FALSE ) + { + client_names[num_clients] = (char*)MALLOC(strlen(tmp_client_name) + 1); + MEMVERIFY( client_names[num_clients] ); + + /* The alsa_pcm client should go in spot 0. If this + * is the alsa_pcm client AND we are NOT about to put + * it in spot 0 put it in spot 0 and move whatever + * was already in spot 0 to the end. */ + if( strcmp( "alsa_pcm", tmp_client_name ) == 0 && num_clients > 0 ) + { + /* alsa_pcm goes in spot 0 */ + strcpy( client_names[ num_clients ], client_names[0] ); + strcpy( client_names[0], "alsa_pcm" ); + num_clients++; + } + else + { + /* put the new client at the end of the client list */ + strcpy( client_names[ num_clients ], tmp_client_name ); + num_clients++; + } + } + } + free( jack_ports ); + + /* Now we have a list of clients, which will become the list of + * PortAudio devices. */ + + commonApi->deviceCount = num_clients; + commonApi->defaultInputDeviceIndex = 0; + commonApi->defaultOutputDeviceIndex = 0; + + /* there is one global sample rate all clients must conform to */ + + globalSampleRate = (double*)MALLOC( sizeof(double) ); + MEMVERIFY( globalSampleRate ); + *globalSampleRate = jack_get_sample_rate( jackApi->jack_client ); + + commonApi->deviceInfos = (PaDeviceInfo**)MALLOC( sizeof(PaDeviceInfo*) * + num_clients ); + MEMVERIFY(commonApi->deviceInfos); + + /* Create a PaDeviceInfo structure for every client */ + for( client_index = 0; client_index < num_clients; client_index++ ) + { + char regex_pattern[100]; + PaDeviceInfo *curDevInfo; + + curDevInfo = (PaDeviceInfo*)MALLOC( sizeof(PaDeviceInfo) ); + MEMVERIFY( curDevInfo ); + + curDevInfo->name = (char*)MALLOC( strlen(client_names[client_index]) + 1 ); + MEMVERIFY( curDevInfo->name ); + strcpy( (char*)curDevInfo->name, client_names[client_index] ); + + curDevInfo->structVersion = 2; + curDevInfo->hostApi = jackApi->hostApiIndex; + + /* JACK is very inflexible: there is one sample rate the whole + * system must run at, and all clients must speak IEEE float. */ + curDevInfo->numSampleRates = 1; + curDevInfo->sampleRates = globalSampleRate; + curDevInfo->nativeSampleFormats = paFloat32|paNonInterleaved; + + /* To determine how many input and output channels are available, + * we re-query jackd with more specific parameters. */ + + sprintf( regex_pattern, "%s:.*", client_names[client_index] ); + + /* ... what are your output ports (that we could input to)? */ + jack_ports = jack_get_ports( jackApi->jack_client, regex_pattern, + NULL, JackPortIsOutput); + curDevInfo->maxInputChannels = 0; + for( i = 0; jack_ports[i] != NULL ; i++) + { + /* The number of ports returned is the number of output channels. + * We don't care what they are, we just care how many */ + curDevInfo->maxInputChannels++; + } + free(jack_ports); + + /* ... what are your input ports (that we could output to)? */ + jack_ports = jack_get_ports( jackApi->jack_client, regex_pattern, + NULL, JackPortIsInput); + curDevInfo->maxOutputChannels = 0; + for( i = 0; jack_ports[i] != NULL ; i++) + { + /* The number of ports returned is the number of input channels. + * We don't care what they are, we just care how many */ + curDevInfo->maxOutputChannels++; + } + free(jack_ports); + + /* Add this client to the list of devices */ + commonApi->deviceInfos[client_index] = curDevInfo; + } + +#undef MALLOC +#undef MEMVERIFY + return paNoError; +} + +PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, + PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + PaJackHostApiRepresentation *jackHostApi; + + jackHostApi = (PaJackHostApiRepresentation*) + PaUtil_AllocateMemory( sizeof(PaJackHostApiRepresentation) ); + if( !jackHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + /* Try to become a client of the JACK server. If we cannot do + * this, than this API cannot be used. */ + + jackHostApi->jack_client = jack_client_new( "PortAudio client" ); + if( jackHostApi->jack_client == 0 ) + { + /* the V19 development docs say that if an implementation + * detects that it cannot be used, it should return a NULL + * interface and paNoError */ + result = paNoError; + *hostApi = NULL; + goto error; + } + + jackHostApi->deviceInfoMemory = PaUtil_CreateAllocationGroup(); + if( !jackHostApi->deviceInfoMemory ) + { + result = paInsufficientMemory; + goto error; + } + + jackHostApi->hostApiIndex = hostApiIndex; + + *hostApi = &jackHostApi->commonHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paInDevelopment; + (*hostApi)->info.name = "JACK Audio Connection Kit"; + + /* Build a device list by querying the JACK server */ + + result = BuildDeviceList( jackHostApi ); + if( result != paNoError ) + goto error; + + /* Register functions */ + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + + PaUtil_InitializeStreamInterface( &jackHostApi->callbackStreamInterface, + CloseStream, StartStream, + StopStream, AbortStream, + IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyReadWrite, PaUtil_DummyReadWrite, + PaUtil_DummyGetAvailable, + PaUtil_DummyGetAvailable ); + + return result; + +error: + if( jackHostApi ) + { + if( jackHostApi->deviceInfoMemory ) + { + PaUtil_FreeAllAllocations( jackHostApi->deviceInfoMemory ); + PaUtil_DestroyAllocationGroup( jackHostApi->deviceInfoMemory ); + } + + PaUtil_FreeMemory( jackHostApi ); + } + return result; +} + + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi; + + jack_client_close( jackHostApi->jack_client ); + + if( jackHostApi->deviceInfoMemory ) + { + PaUtil_FreeAllAllocations( jackHostApi->deviceInfoMemory ); + PaUtil_DestroyAllocationGroup( jackHostApi->deviceInfoMemory ); + } + + PaUtil_FreeMemory( jackHostApi ); +} + + +/* PaJackStream - a stream data structure specifically for this implementation */ + +typedef struct PaJackStream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilBufferProcessor bufferProcessor; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + + /* our input and output ports */ + jack_port_t **local_input_ports; + jack_port_t **local_output_ports; + + /* the input and output ports of the client we are connecting to */ + jack_port_t **remote_input_ports; + jack_port_t **remote_output_ports; + + int num_incoming_connections; + int num_outgoing_connections; + + jack_client_t *jack_client; + + /* The stream is running if it's still producing samples. + * The stream is active if samples it produced are still being heard. + */ + int is_running; + int is_active; + + jack_nframes_t t0; + unsigned long total_frames_sent; + + PaUtilAllocationGroup *stream_memory; +} +PaJackStream; + +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + PaDeviceIndex inputDevice, + int numInputChannels, + PaSampleFormat inputSampleFormat, + unsigned long inputLatency, + PaHostApiSpecificStreamInfo *inputStreamInfo, + PaDeviceIndex outputDevice, + int numOutputChannels, + PaSampleFormat outputSampleFormat, + unsigned long outputLatency, + PaHostApiSpecificStreamInfo *outputStreamInfo, + double sampleRate, + unsigned long framesPerCallback, + PaStreamFlags streamFlags, + PortAudioCallback *callback, + void *userData ) +{ + PaError result = paNoError; + PaJackHostApiRepresentation *jackHostApi = (PaJackHostApiRepresentation*)hostApi; + PaJackStream *stream = 0; + char port_string[100]; + char regex_pattern[100]; + const char **jack_ports; + int jack_max_buffer_size = jack_get_buffer_size( jackHostApi->jack_client ); + int i; + + /* the client has no say over the frames per callback */ + + (void)framesPerCallback; + + /* Preliminary checks */ + + /* ... check that input device can support numInputChannels */ + + if( inputDevice != paNoDevice && + numInputChannels > hostApi->deviceInfos[ inputDevice ]->maxInputChannels ) + return paInvalidChannelCount; + + /* ... check that output device can support numOutputChannels */ + + if( outputDevice != paNoDevice && + numOutputChannels > hostApi->deviceInfos[ outputDevice ]->maxOutputChannels) + return paInvalidChannelCount; + + /* ... check that the sample rate exactly matches the ONE acceptable rate */ + +#define ABS(x) ( (x) > 0 ? (x) : -(x) ) + if( ABS(sampleRate - hostApi->deviceInfos[0]->sampleRates[0]) > 1 ) + return paInvalidSampleRate; +#undef ABS + + /* ... this implementation doesn't use custom stream info */ + + if( inputStreamInfo ) + return paIncompatibleStreamInfo; + + /* ... this implementation doesn't use custom stream info */ + + if( outputStreamInfo ) + return paIncompatibleStreamInfo; + + /* ... this implementation doesn't use platform-specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; + + /* Allocate memory for structuures */ + +#define MALLOC(size) \ + (PaUtil_GroupAllocateMemory( stream->stream_memory, (size) )) + +#define MEMVERIFY(ptr) \ + if( (ptr) == NULL ) \ + { \ + result = paInsufficientMemory; \ + goto error; \ + } + + stream = (PaJackStream*)PaUtil_AllocateMemory( sizeof(PaJackStream) ); + MEMVERIFY( stream ); + + stream->stream_memory = PaUtil_CreateAllocationGroup(); + stream->jack_client = jackHostApi->jack_client; + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + stream->local_input_ports = + (jack_port_t**) MALLOC(sizeof(jack_port_t*) * numInputChannels ); + stream->local_output_ports = + (jack_port_t**) MALLOC( sizeof(jack_port_t*) * numOutputChannels ); + stream->remote_output_ports = + (jack_port_t**) MALLOC( sizeof(jack_port_t*) * numInputChannels ); + stream->remote_input_ports = + (jack_port_t**) MALLOC( sizeof(jack_port_t*) * numOutputChannels ); + + MEMVERIFY( stream->local_input_ports ); + MEMVERIFY( stream->local_output_ports ); + MEMVERIFY( stream->remote_input_ports ); + MEMVERIFY( stream->remote_output_ports ); + + stream->num_incoming_connections = numInputChannels; + stream->num_outgoing_connections = numOutputChannels; + + if( callback ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &jackHostApi->callbackStreamInterface, callback, userData ); + } + else + { + /* we do not support blocking I/O */ + return paBadIODeviceCombination; + } + + /* create the JACK ports. We cannot connect them until audio + * processing begins */ + + for( i = 0; i < numInputChannels; i++ ) + { + sprintf( port_string, "in_%d", i ); + stream->local_input_ports[i] = jack_port_register( + jackHostApi->jack_client, port_string, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + } + + for( i = 0; i < numOutputChannels; i++ ) + { + sprintf( port_string, "out_%d", i ); + stream->local_output_ports[i] = jack_port_register( + jackHostApi->jack_client, port_string, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + } + + /* look up the jack_port_t's for the remote ports. We could do + * this at stream start time, but doing it here ensures the + * name lookup only happens once. */ + + if( numInputChannels > 0 ) + { + /* ... remote output ports (that we input from) */ + sprintf( regex_pattern, "%s:.*", hostApi->deviceInfos[ inputDevice ]->name ); + jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, + NULL, JackPortIsOutput); + for( i = 0; i < numInputChannels && jack_ports[i]; i++ ) + { + stream->remote_output_ports[i] = jack_port_by_name( + jackHostApi->jack_client, jack_ports[i] ); + } + if( i < numInputChannels ) + { + /* we found fewer ports than we expected */ + return paInternalError; + } + free( jack_ports ); // XXX: this doesn't happen if we exit prematurely + } + + + if( numOutputChannels > 0 ) + { + /* ... remote input ports (that we output to) */ + sprintf( regex_pattern, "%s:.*", hostApi->deviceInfos[ outputDevice ]->name ); + jack_ports = jack_get_ports( jackHostApi->jack_client, regex_pattern, + NULL, JackPortIsInput); + for( i = 0; i < numOutputChannels && jack_ports[i]; i++ ) + { + stream->remote_input_ports[i] = jack_port_by_name( + jackHostApi->jack_client, jack_ports[i] ); + } + if( i < numOutputChannels ) + { + /* we found fewer ports than we expected */ + return paInternalError; + } + free( jack_ports ); // XXX: this doesn't happen if we exit prematurely + } + + result = PaUtil_InitializeBufferProcessor( + &stream->bufferProcessor, + numInputChannels, + inputSampleFormat, + paFloat32, /* hostInputSampleFormat */ + numOutputChannels, + outputSampleFormat, + paFloat32, /* hostOutputSampleFormat */ + sampleRate, + streamFlags, + framesPerCallback, + jack_max_buffer_size, + paUtilFixedHostBufferSize, + callback, + userData ); + + if( result != paNoError ) + goto error; + + stream->is_running = FALSE; + stream->t0 = -1;/* set the first time through the callback*/ + stream->total_frames_sent = 0; + + jack_set_process_callback( jackHostApi->jack_client, JackCallback, stream ); + + *s = (PaStream*)stream; + + return result; + +error: + if( stream ) + { + if( stream->stream_memory ) + { + PaUtil_FreeAllAllocations( stream->stream_memory ); + PaUtil_DestroyAllocationGroup( stream->stream_memory ); + } + + PaUtil_FreeMemory( stream ); + } + + return result; + +#undef MALLOC +#undef MEMVERIFY +} + + +static int JackCallback( jack_nframes_t frames, void *userData ) +{ + PaJackStream *stream = (PaJackStream*)userData; + PaTimestamp outTime = 0; /* IMPLEMENT ME */ + int callbackResult; + int chn; + int framesProcessed; + + if( stream->t0 == -1 ) + { + if( stream->num_outgoing_connections == 0 ) + { + /* TODO: how to handle stream time for capture-only operation? */ + } + else + { + /* the beginning time needs to be initialized */ + stream->t0 = jack_frame_time( stream->jack_client ) - + jack_frames_since_cycle_start( stream->jack_client) + + jack_port_get_total_latency( stream->jack_client, + stream->local_output_ports[0] ); + } + } + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, outTime ); + + for( chn = 0; chn < stream->num_incoming_connections; chn++ ) + { + jack_default_audio_sample_t *channel_buf; + channel_buf = (jack_default_audio_sample_t*) + jack_port_get_buffer( stream->local_input_ports[chn], + frames ); + + PaUtil_SetNonInterleavedInputChannel( &stream->bufferProcessor, + chn, + channel_buf ); + } + + for( chn = 0; chn < stream->num_outgoing_connections; chn++ ) + { + jack_default_audio_sample_t *channel_buf; + channel_buf = (jack_default_audio_sample_t*) + jack_port_get_buffer( stream->local_output_ports[chn], + frames ); + + PaUtil_SetNonInterleavedOutputChannel( &stream->bufferProcessor, + chn, + channel_buf ); + } + + if( stream->num_incoming_connections > 0 ) + PaUtil_SetInputFrameCount( &stream->bufferProcessor, frames ); + + if( stream->num_outgoing_connections > 0 ) + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, frames ); + + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, + &callbackResult ); + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + stream->total_frames_sent += frames; + + + if( callbackResult == paContinue ) + { + /* nothing special */ + } + else if( callbackResult == paAbort ) + { + /* finish playback immediately */ + + /* TODO: memset 0 the outgoing samples to "cancel" them */ + + stream->is_active = FALSE; + + /* return nonzero so we get deactivated (and the callback won't + * get called again) */ + return 1; + } + else + { + /* User callback has asked us to stop with paComplete or other non-zero value. */ + + stream->is_active = FALSE; + + /* return nonzero so we get deactivated (and the callback won't + * get called again) */ + return 1; + } + return 0; +} + + +/* + 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; + PaJackStream *stream = (PaJackStream*)s; + + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + + return result; +} + + +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaJackStream *stream = (PaJackStream*)s; + int i; + + /* start the audio thread */ + + jack_activate( stream->jack_client ); + + /* connect the ports */ + + /* NOTE: I would rather use jack_port_connect which uses jack_port_t's + * instead of port names, but it is not implemented yet. */ + if( stream->num_incoming_connections > 0 ) + { + for( i = 0; i < stream->num_incoming_connections; i++ ) + jack_connect( stream->jack_client, + jack_port_name(stream->remote_output_ports[i]), + jack_port_name(stream->local_input_ports[i] ) ); + } + + if( stream->num_outgoing_connections > 0 ) + { + for( i = 0; i < stream->num_outgoing_connections; i++ ) + jack_connect( stream->jack_client, + jack_port_name(stream->local_output_ports[i]), + jack_port_name(stream->remote_input_ports[i]) ); + } + + stream->is_running = TRUE; + + return result; +} + + +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaJackStream *stream = (PaJackStream*)s; + + /* note: this automatically disconnects all ports, since a deactivated + * client is not allowed to have any ports connected */ + jack_deactivate( stream->jack_client ); + + stream->is_running = FALSE; + + /* TODO: block until playback complete */ + + stream->is_active = FALSE; + + return result; +} + + +static PaError AbortStream( PaStream *s ) +{ + PaError result = paNoError; + PaJackStream *stream = (PaJackStream*)s; + + /* There's no way to cancel samples already submitted, but we can + * return immediately */ + + /* note: this automatically disconnects all ports, since a deactivated + * client is not allowed to have any ports connected */ + jack_deactivate( stream->jack_client ); + + stream->is_running = FALSE; + stream->is_active = FALSE; + + return result; +} + + +static PaError IsStreamStopped( PaStream *s ) +{ + PaJackStream *stream = (PaJackStream*)s; + + return stream->is_running == FALSE; +} + + +static PaError IsStreamActive( PaStream *s ) +{ + PaJackStream *stream = (PaJackStream*)s; + + return stream->is_active == TRUE; +} + + +static PaTimestamp GetStreamTime( PaStream *s ) +{ + PaJackStream *stream = (PaJackStream*)s; + + /* TODO: what if we're recording-only? */ + return jack_frame_time( stream->jack_client ) - stream->t0; +} + + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaJackStream *stream = (PaJackStream*)s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + + diff --git a/pd/portaudio/pa_linux_alsa/blocking_calls.c b/pd/portaudio/pa_linux_alsa/blocking_calls.c new file mode 100644 index 00000000..6304b117 --- /dev/null +++ b/pd/portaudio/pa_linux_alsa/blocking_calls.c @@ -0,0 +1,61 @@ + +#include "pa_stream.h" + +#include "pa_linux_alsa.h" + +PaError ReadStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + /* TODO: handle failure, xruns */ + + if( stream->capture_interleaved ) + { + snd_pcm_mmap_readi( stream->pcm_capture, buffer, frames ); + } + else + { + snd_pcm_mmap_readn( stream->pcm_capture, (void**)buffer, frames ); + } + + return paNoError; +} + + +PaError WriteStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + if( stream->playback_interleaved ) + { + snd_pcm_mmap_writei( stream->pcm_playback, buffer, frames ); + } + else + { + snd_pcm_mmap_writen( stream->pcm_playback, (void**)buffer, frames ); + } + + return paNoError; +} + + +unsigned long GetStreamReadAvailable( PaStream* s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + return snd_pcm_avail_update( stream->pcm_capture ); +} + + +unsigned long GetStreamWriteAvailable( PaStream* s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + return snd_pcm_avail_update( stream->pcm_playback ); +} + + diff --git a/pd/portaudio/pa_linux_alsa/blocking_calls.o b/pd/portaudio/pa_linux_alsa/blocking_calls.o new file mode 100644 index 00000000..d4bc2108 Binary files /dev/null and b/pd/portaudio/pa_linux_alsa/blocking_calls.o differ diff --git a/pd/portaudio/pa_linux_alsa/callback_thread.c b/pd/portaudio/pa_linux_alsa/callback_thread.c new file mode 100644 index 00000000..483557b6 --- /dev/null +++ b/pd/portaudio/pa_linux_alsa/callback_thread.c @@ -0,0 +1,374 @@ + +#include +#include +#include /* abs() */ + +#include + +#include "pa_linux_alsa.h" + +#define MIN(x,y) ( (x) < (y) ? (x) : (y) ) + +static int wait( PaAlsaStream *stream ) +{ + int need_capture; + int need_playback; + int capture_avail = INT_MAX; + int playback_avail = INT_MAX; + int common_avail; + + if( stream->pcm_capture ) + need_capture = 1; + else + need_capture = 0; + + if( stream->pcm_playback ) + need_playback = 1; + else + need_playback = 0; + + while( need_capture || need_playback ) + { + int playback_pfd_offset=0; + int total_fds = 0; + + /* if the main thread has requested that we stop, do so now */ + pthread_testcancel(); + + /*printf("still polling...\n"); + if( need_capture ) + printf("need capture.\n"); + if( need_playback ) + printf("need playback.\n"); */ + + /* get the fds, packing all applicable fds into a single array, + * so we can check them all with a single poll() call */ + + if( need_capture ) + { + snd_pcm_poll_descriptors( stream->pcm_capture, stream->pfds, + stream->capture_nfds ); + total_fds += stream->capture_nfds; + } + + if( need_playback ) + { + playback_pfd_offset = total_fds; + snd_pcm_poll_descriptors( stream->pcm_playback, + stream->pfds + playback_pfd_offset, + stream->playback_nfds ); + total_fds += stream->playback_nfds; + } + + /* now poll on the combination of playback and capture fds. + * TODO: handle interrupt and/or failure */ + poll( stream->pfds, total_fds, 1000 ); + + /* check the return status of our pfds */ + if( need_capture ) + { + short revents; + snd_pcm_poll_descriptors_revents( stream->pcm_capture, stream->pfds, + stream->capture_nfds, &revents ); + if( revents == POLLIN ) + need_capture = 0; + } + + if( need_playback ) + { + short revents; + snd_pcm_poll_descriptors_revents( stream->pcm_playback, + stream->pfds + playback_pfd_offset, + stream->playback_nfds, &revents ); + //if( revents & POLLOUT ) + //if( revents & POLLERR ) + // printf("polling error!"); + if( revents == POLLOUT ) + need_playback = 0; + } + } + + /* we have now established that there are buffers ready to be + * operated on. Now determine how many frames are available. */ + if( stream->pcm_capture ) + capture_avail = snd_pcm_avail_update( stream->pcm_capture ); + + if( stream->pcm_playback ) + playback_avail = snd_pcm_avail_update( stream->pcm_playback ); + + common_avail = MIN(capture_avail, playback_avail); + common_avail -= common_avail % stream->frames_per_period; + + return common_avail; +} + +static int setup_buffers( PaAlsaStream *stream, int frames_avail ) +{ + int i; + int capture_frames_avail = INT_MAX; + int playback_frames_avail = INT_MAX; + int common_frames_avail; + + if( stream->pcm_capture ) + { + const snd_pcm_channel_area_t *capture_areas; + const snd_pcm_channel_area_t *area; + snd_pcm_uframes_t frames = frames_avail; + + /* I do not understand this code fragment yet, it is copied out of the + * alsa-devel archives... */ + snd_pcm_mmap_begin( stream->pcm_capture, &capture_areas, + &stream->capture_offset, &frames); + + if( stream->capture_interleaved ) + { + void *interleaved_capture_buffer; + area = &capture_areas[0]; + interleaved_capture_buffer = area->addr + + (area->first + area->step * stream->capture_offset) / 8; + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, + 0, /* starting at channel 0 */ + interleaved_capture_buffer, + 0 /* default numInputChannels */ + ); + } + else + { + /* noninterleaved */ + void *noninterleaved_capture_buffers[1000]; + for( i = 0; i < stream->capture_channels; i++ ) + { + area = &capture_areas[i]; + noninterleaved_capture_buffers[i] = area->addr + + (area->first + area->step * stream->capture_offset) / 8; + PaUtil_SetNonInterleavedInputChannel( &stream->bufferProcessor, + i, + noninterleaved_capture_buffers[i]); + } + } + + capture_frames_avail = frames; + } + + if( stream->pcm_playback ) + { + const snd_pcm_channel_area_t *playback_areas; + const snd_pcm_channel_area_t *area; + snd_pcm_uframes_t frames = frames_avail; + + /* I do not understand this code fragment yet, it is copied out of the + * alsa-devel archives... */ + snd_pcm_mmap_begin( stream->pcm_playback, &playback_areas, + &stream->playback_offset, &frames); + + if( stream->playback_interleaved ) + { + void *interleaved_playback_buffer; + area = &playback_areas[0]; + interleaved_playback_buffer = area->addr + + (area->first + area->step * stream->playback_offset) / 8; + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, + 0, /* starting at channel 0 */ + interleaved_playback_buffer, + 0 /* default numInputChannels */ + ); + } + else + { + /* noninterleaved */ + void *noninterleaved_playback_buffers[1000]; + for( i = 0; i < stream->playback_channels; i++ ) + { + area = &playback_areas[i]; + noninterleaved_playback_buffers[i] = area->addr + + (area->first + area->step * stream->playback_offset) / 8; + PaUtil_SetNonInterleavedOutputChannel( &stream->bufferProcessor, + i, + noninterleaved_playback_buffers[i]); + } + } + + playback_frames_avail = frames; + } + + + common_frames_avail = MIN(capture_frames_avail, playback_frames_avail); + common_frames_avail -= common_frames_avail % stream->frames_per_period; + //printf( "%d capture frames available\n", capture_frames_avail ); + //printf( "%d frames playback available\n", playback_frames_avail ); + //printf( "%d frames available\n", common_frames_avail ); + + if( stream->pcm_capture ) + PaUtil_SetInputFrameCount( &stream->bufferProcessor, common_frames_avail ); + + if( stream->pcm_playback ) + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, common_frames_avail ); + + return common_frames_avail; +} + +void *CallbackThread( void *userData ) +{ + PaAlsaStream *stream = (PaAlsaStream*)userData; + + if( stream->pcm_capture ) + snd_pcm_start( stream->pcm_capture ); + if( stream->pcm_playback ) + snd_pcm_start( stream->pcm_playback ); + + while(1) + { + int frames_avail; + int frames_got; + + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* IMPLEMENT ME */ + int callbackResult; + int framesProcessed; + + pthread_testcancel(); + { + /* calculate time info */ + snd_timestamp_t capture_timestamp; + snd_timestamp_t playback_timestamp; + snd_pcm_status_t *capture_status; + snd_pcm_status_t *playback_status; + snd_pcm_status_alloca( &capture_status ); + snd_pcm_status_alloca( &playback_status ); + + if( stream->pcm_capture ) + { + snd_pcm_status( stream->pcm_capture, capture_status ); + snd_pcm_status_get_tstamp( capture_status, &capture_timestamp ); + } + if( stream->pcm_playback ) + { + snd_pcm_status( stream->pcm_playback, playback_status ); + snd_pcm_status_get_tstamp( playback_status, &playback_timestamp ); + } + + /* Hmm, we potentially have both a playback and a capture timestamp. + * Hopefully they are the same... */ + if( stream->pcm_capture && stream->pcm_playback ) + { + float capture_time = capture_timestamp.tv_sec + + ((float)capture_timestamp.tv_usec/1000000); + float playback_time= playback_timestamp.tv_sec + + ((float)playback_timestamp.tv_usec/1000000); + if( fabsf(capture_time-playback_time) > 0.01 ) + printf("Capture time and playback time differ by %f\n", fabsf(capture_time-playback_time)); + timeInfo.currentTime = capture_time; + } + else if( stream->pcm_playback ) + { + timeInfo.currentTime = playback_timestamp.tv_sec + + ((float)playback_timestamp.tv_usec/1000000); + } + else + { + timeInfo.currentTime = capture_timestamp.tv_sec + + ((float)capture_timestamp.tv_usec/1000000); + } + + if( stream->pcm_capture ) + { + snd_pcm_sframes_t capture_delay = snd_pcm_status_get_delay( capture_status ); + timeInfo.inputBufferAdcTime = timeInfo.currentTime - + (float)capture_delay / stream->streamRepresentation.streamInfo.sampleRate; + } + + if( stream->pcm_playback ) + { + snd_pcm_sframes_t playback_delay = snd_pcm_status_get_delay( playback_status ); + timeInfo.outputBufferDacTime = timeInfo.currentTime + + (float)playback_delay / stream->streamRepresentation.streamInfo.sampleRate; + } + } + + + /* + IMPLEMENT ME: + - handle buffer slips + */ + + /* + depending on whether the host buffers are interleaved, non-interleaved + or a mixture, you will want to call PaUtil_ProcessInterleavedBuffers(), + PaUtil_ProcessNonInterleavedBuffers() or PaUtil_ProcessBuffers() here. + */ + + frames_avail = wait( stream ); + //printf( "%d frames available\n", frames_avail ); + + /* Now we know the soundcard is ready to produce/receive at least + * one period. We just need to get the buffers for the client + * to read/write. */ + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo ); + + frames_got = setup_buffers( stream, frames_avail ); + + if( frames_avail == frames_got ) + ;//printf("good, they were both %d\n", frames_avail ); + else + printf("damn, they were different: avail: %d, got: %d\n", frames_avail, frames_got ); + + /* this calls the callback */ + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, + &callbackResult ); + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + + /* inform ALSA how many frames we wrote */ + + if( stream->pcm_capture ) + snd_pcm_mmap_commit( stream->pcm_capture, stream->capture_offset, frames_avail ); + + if( stream->pcm_playback ) + snd_pcm_mmap_commit( stream->pcm_playback, stream->playback_offset, frames_avail ); + + + /* + If you need to byte swap outputBuffer, you can do it here using + routines in pa_byteswappers.h + */ + + if( callbackResult == paContinue ) + { + /* nothing special to do */ + } + else if( callbackResult == paAbort ) + { + stream->callback_finished = 1; + + if( stream->pcm_capture ) + { + snd_pcm_drop( stream->pcm_capture ); + } + + if( stream->pcm_playback ) + { + snd_pcm_drop( stream->pcm_playback ); + } + pthread_exit(NULL); + } + else + { + stream->callback_finished = 1; + + if( stream->pcm_capture ) + { + snd_pcm_drain( stream->pcm_capture ); + } + + if( stream->pcm_playback ) + { + snd_pcm_drain( stream->pcm_playback ); + } + pthread_exit(NULL); + } + + } +} + diff --git a/pd/portaudio/pa_linux_alsa/callback_thread.o b/pd/portaudio/pa_linux_alsa/callback_thread.o new file mode 100644 index 00000000..a242d490 Binary files /dev/null and b/pd/portaudio/pa_linux_alsa/callback_thread.o differ diff --git a/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c new file mode 100644 index 00000000..9582b5b8 --- /dev/null +++ b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.c @@ -0,0 +1,989 @@ +/* + * $Id: pa_linux_alsa.c,v 1.1.2.3 2003/02/01 21:55:03 joshua Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * ALSA implementation by Joshua Haberman + * + * Copyright (c) 2002 Joshua Haberman + * + * 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. + */ + +#include +#include + +#include /* strlen() */ +#include + +#include + +#include "portaudio.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 "pa_linux_alsa.h" + +/* PaAlsaHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct +{ + PaUtilHostApiRepresentation commonHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + PaHostApiIndex hostApiIndex; +} +PaAlsaHostApiRepresentation; + + +/* prototypes for functions declared in this file */ + +PaError PaAlsa_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 *callback, + void *userData ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); +static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi ); + +/* blocking calls are in blocking_calls.c */ +extern PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); +extern PaError WriteStream( PaStream* stream, void *buffer, unsigned long frames ); +extern signed long GetStreamReadAvailable( PaStream* stream ); +extern signed long GetStreamWriteAvailable( PaStream* stream ); + +/* all callback-related functions are in callback_thread.c */ +extern void *CallbackThread( void *userData ); + + +PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + int i, deviceCount; + PaAlsaHostApiRepresentation *skeletonHostApi; + + skeletonHostApi = (PaAlsaHostApiRepresentation*) + PaUtil_AllocateMemory( sizeof(PaAlsaHostApiRepresentation) ); + if( !skeletonHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + skeletonHostApi->allocations = PaUtil_CreateAllocationGroup(); + if( !skeletonHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + skeletonHostApi->hostApiIndex = hostApiIndex; + *hostApi = (PaUtilHostApiRepresentation*)skeletonHostApi; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paALSA; + (*hostApi)->info.name = "ALSA implementation"; + + BuildDeviceList( skeletonHostApi ); + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + + PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, + CloseStream, StartStream, + StopStream, AbortStream, + IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyReadWrite, PaUtil_DummyReadWrite, + PaUtil_DummyGetAvailable, + PaUtil_DummyGetAvailable ); + + PaUtil_InitializeStreamInterface( &skeletonHostApi->blockingStreamInterface, + CloseStream, StartStream, + StopStream, AbortStream, + IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, + GetStreamReadAvailable, + GetStreamWriteAvailable ); + + return result; + +error: + if( skeletonHostApi ) + { + if( skeletonHostApi->allocations ) + { + PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); + PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); + } + + PaUtil_FreeMemory( skeletonHostApi ); + } + return result; +} + +static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) +{ + PaUtilHostApiRepresentation *commonApi = &alsaApi->commonHostApiRep; + PaDeviceInfo *deviceInfoArray; + int deviceCount = 0; + int card_idx; + int device_idx; + snd_ctl_t *ctl; + snd_ctl_card_info_t *card_info; + + /* count the devices by enumerating all the card numbers */ + + /* snd_card_next() modifies the integer passed to it to be: + * the index of the first card if the parameter is -1 + * the index of the next card if the parameter is the index of a card + * -1 if there are no more cards + * + * The function itself returns 0 if it succeeded. */ + card_idx = -1; + while( snd_card_next( &card_idx ) == 0 && card_idx >= 0 ) + { + deviceCount++; + } + + /* allocate deviceInfo memory based on the number of devices */ + + commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + alsaApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ); + if( !commonApi->deviceInfos ) + { + return paInsufficientMemory; + } + + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( + alsaApi->allocations, sizeof(PaDeviceInfo) * deviceCount ); + if( !deviceInfoArray ) + { + return paInsufficientMemory; + } + + /* now loop over the list of devices again, filling in the deviceInfo for each */ + card_idx = -1; + device_idx = 0; + while( snd_card_next( &card_idx ) == 0 && card_idx >= 0 ) + { + PaDeviceInfo *deviceInfo = &deviceInfoArray[device_idx]; + char *deviceName; + char alsaDeviceName[50]; + const char *cardName; + + commonApi->deviceInfos[device_idx++] = deviceInfo; + + deviceInfo->structVersion = 2; + deviceInfo->hostApi = alsaApi->hostApiIndex; + + sprintf( alsaDeviceName, "hw:%d", card_idx ); + snd_ctl_open( &ctl, alsaDeviceName, 0 ); + snd_ctl_card_info_malloc( &card_info ); + snd_ctl_card_info( ctl, card_info ); + cardName = snd_ctl_card_info_get_id( card_info ); + + deviceName = (char*)PaUtil_GroupAllocateMemory( alsaApi->allocations, + strlen(cardName) + 1 ); + if( !deviceName ) + { + return paInsufficientMemory; + } + strcpy( deviceName, cardName ); + deviceInfo->name = deviceName; + + snd_ctl_card_info_free( card_info ); + + /* to determine max. channels, we must open the device and query the + * hardware parameter configuration space */ + { + snd_pcm_t *pcm_handle; + snd_pcm_hw_params_t *hw_params; + int dir; + + snd_pcm_hw_params_malloc( &hw_params ); + + /* get max channels for capture */ + + if( snd_pcm_open( &pcm_handle, alsaDeviceName, SND_PCM_STREAM_CAPTURE, 0 ) < 0 ) + { + deviceInfo->maxInputChannels = 0; + } + else + { + snd_pcm_hw_params_any( pcm_handle, hw_params ); + deviceInfo->maxInputChannels = snd_pcm_hw_params_get_channels_max( hw_params ); + /* TODO: I'm not really sure what to do here */ + //deviceInfo->defaultLowInputLatency = snd_pcm_hw_params_get_period_size_min( hw_params, &dir ); + //deviceInfo->defaultHighInputLatency = snd_pcm_hw_params_get_period_size_max( hw_params, &dir ); + deviceInfo->defaultLowInputLatency = 128. / 44100; + deviceInfo->defaultHighInputLatency = 16384. / 44100; + snd_pcm_close( pcm_handle ); + } + + /* get max channels for playback */ + if( snd_pcm_open( &pcm_handle, alsaDeviceName, SND_PCM_STREAM_PLAYBACK, 0 ) < 0 ) + { + deviceInfo->maxOutputChannels = 0; + } + else + { + snd_pcm_hw_params_any( pcm_handle, hw_params ); + deviceInfo->maxOutputChannels = snd_pcm_hw_params_get_channels_max( hw_params ); + /* TODO: I'm not really sure what to do here */ + //deviceInfo->defaultLowOutputLatency = snd_pcm_hw_params_get_period_size_min( hw_params, &dir ); + //deviceInfo->defaultHighOutputLatency = snd_pcm_hw_params_get_period_size_max( hw_params, &dir ); + deviceInfo->defaultLowOutputLatency = 128. / 44100; + deviceInfo->defaultHighOutputLatency = 16384. / 44100; + snd_pcm_close( pcm_handle ); + } + + snd_pcm_hw_params_free( hw_params ); + } + + deviceInfo->defaultSampleRate = 44100.; /* IMPLEMENT ME */ + } + + commonApi->info.deviceCount = deviceCount; + commonApi->info.defaultInputDevice = 0; + commonApi->info.defaultOutputDevice = 0; + + return paNoError; +} + + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaAlsaHostApiRepresentation *skeletonHostApi; + skeletonHostApi = (PaAlsaHostApiRepresentation*)hostApi; + + /* + IMPLEMENT ME: + - clean up any resourced not handled by the allocation group + */ + + if( skeletonHostApi->allocations ) + { + PaUtil_FreeAllAllocations( skeletonHostApi->allocations ); + PaUtil_DestroyAllocationGroup( skeletonHostApi->allocations ); + } + + PaUtil_FreeMemory( skeletonHostApi ); +} + + +/* Given an open stream, what sample formats are available? */ + +static PaSampleFormat GetAvailableFormats( snd_pcm_t *stream ) +{ + PaSampleFormat available = 0; + snd_pcm_hw_params_t *hw_params; + snd_pcm_hw_params_alloca( &hw_params ); + + snd_pcm_hw_params_any( stream, hw_params ); + + if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_FLOAT ) == 0) + available |= paFloat32; + + if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_S16 ) == 0) + available |= paInt16; + + if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_S24 ) == 0) + available |= paInt24; + + if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_S32 ) == 0) + available |= paInt32; + + if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_S8 ) == 0) + available |= paInt8; + + if( snd_pcm_hw_params_test_format( stream, hw_params, SND_PCM_FORMAT_U8 ) == 0) + available |= paUInt8; + + return available; +} + +/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ + +static PaError ConfigureStream( snd_pcm_t *stream, int channels, + int interleaved, unsigned long rate, + PaSampleFormat pa_format, int framesPerBuffer ) +{ +#define ENSURE(functioncall) \ + if( (functioncall) < 0 ) { \ + printf("Error executing ALSA call, line %d\n", __LINE__); \ + return 1; \ + } \ + else { \ + printf("ALSA call at line %d succeeded\n", __LINE__ ); \ + } + + snd_pcm_access_t access_mode; + snd_pcm_format_t alsa_format; + + /* configuration consists of setting all of ALSA's parameters. + * These parameters come in two flavors: hardware parameters + * and software paramters. Hardware parameters will affect + * the way the device is initialized, software parameters + * affect the way ALSA interacts with me, the user-level client. */ + + snd_pcm_hw_params_t *hw_params; + snd_pcm_sw_params_t *sw_params; + + snd_pcm_hw_params_alloca( &hw_params ); + + /* ... fill up the configuration space with all possibile + * combinations of parameters this device will accept */ + ENSURE( snd_pcm_hw_params_any( stream, hw_params ) ); + + if( interleaved ) + access_mode = SND_PCM_ACCESS_MMAP_INTERLEAVED; + else + access_mode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; + + ENSURE( snd_pcm_hw_params_set_access( stream, hw_params, access_mode ) ); + + /* set the format based on what the user selected */ + switch( pa_format ) + { + case paFloat32: + alsa_format = SND_PCM_FORMAT_FLOAT; + break; + + case paInt16: + alsa_format = SND_PCM_FORMAT_S16; + break; + + case paInt24: + alsa_format = SND_PCM_FORMAT_S24; + break; + + case paInt32: + alsa_format = SND_PCM_FORMAT_S32; + break; + + case paInt8: + alsa_format = SND_PCM_FORMAT_S8; + break; + + case paUInt8: + alsa_format = SND_PCM_FORMAT_U8; + break; + + default: + printf("Unknown PortAudio format %d\n", (int)pa_format ); + return 1; + } + //printf("PortAudio format: %d\n", pa_format); + printf("ALSA format: %d\n", alsa_format); + ENSURE( snd_pcm_hw_params_set_format( stream, hw_params, alsa_format ) ); + + /* ... set the sample rate */ + ENSURE( snd_pcm_hw_params_set_rate( stream, hw_params, rate, 0 ) ); + + /* ... set the number of channels */ + ENSURE( snd_pcm_hw_params_set_channels( stream, hw_params, channels ) ); + + /* ... set the number of periods to 2, which is essentially double buffering. + * this makes the latency the number of samples per buffer, which is the best + * it can be */ + ENSURE( snd_pcm_hw_params_set_periods ( stream, hw_params, 2, 0 ) ); + + /* ... set the period size, which is essentially the hardware buffer size */ + if( framesPerBuffer != 0 ) + { + ENSURE( snd_pcm_hw_params_set_period_size( stream, hw_params, + framesPerBuffer, 0 ) ); + } + else + { + ENSURE( snd_pcm_hw_params_set_period_size( stream, hw_params, + 2048, 0 ) ); + } + + + /* Set the parameters! */ + ENSURE( snd_pcm_hw_params( stream, hw_params ) ); + + return 0; +#undef ENSURE +} + +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *callback, + void *userData ) +{ + PaError result = paNoError; + PaAlsaHostApiRepresentation *skeletonHostApi = + (PaAlsaHostApiRepresentation*)hostApi; + PaAlsaStream *stream = 0; + PaSampleFormat hostInputSampleFormat=0, hostOutputSampleFormat=0; + int numInputChannels, numOutputChannels; + PaSampleFormat inputSampleFormat=0, outputSampleFormat=0; + unsigned long framesPerHostBuffer = framesPerBuffer; + + if( framesPerHostBuffer == paFramesPerBufferUnspecified ) + { + // TODO: have some reason + framesPerHostBuffer = 2048; + } + + if( inputParameters ) + { + numInputChannels = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification. + [JH] this could be supported in the future, to allow ALSA device strings + like hw:0 */ + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support numInputChannels */ + if( numInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + numInputChannels = 0; + } + + if( outputParameters ) + { + numOutputChannels = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification + [JH] this could be supported in the future, to allow ALSA device strings + like hw:0 */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support numInputChannels */ + if( numOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + numOutputChannels = 0; + } + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + /* allocate and do basic initialization of the stream structure */ + + stream = (PaAlsaStream*)PaUtil_AllocateMemory( sizeof(PaAlsaStream) ); + if( !stream ) + { + printf("memory point 2\n"); + result = paInsufficientMemory; + goto error; + } + + stream->pcm_capture = NULL; + stream->pcm_playback = NULL; + stream->callback_mode = (callback != 0); + stream->callback_finished = 0; + + if( callback ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &skeletonHostApi->callbackStreamInterface, + callback, userData ); + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &skeletonHostApi->blockingStreamInterface, + callback, userData ); + } + + + stream->streamRepresentation.streamInfo.inputLatency = framesPerHostBuffer; + stream->streamRepresentation.streamInfo.outputLatency = framesPerHostBuffer; + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + /* open the devices now, so we can obtain info about the available formats */ + + if( numInputChannels > 0 ) + { + char inputDeviceName[50]; + + sprintf( inputDeviceName, "hw:CARD=%s", hostApi->deviceInfos[inputParameters->device]->name ); + if( snd_pcm_open( &stream->pcm_capture, inputDeviceName, SND_PCM_STREAM_CAPTURE, 0 ) < 0 ) + { + result = paBadIODeviceCombination; + goto error; + } + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( GetAvailableFormats(stream->pcm_capture), + inputSampleFormat ); + } + + if( numOutputChannels > 0 ) + { + char outputDeviceName[50]; + + sprintf( outputDeviceName, "hw:CARD=%s", hostApi->deviceInfos[outputParameters->device]->name ); + if( snd_pcm_open( &stream->pcm_playback, outputDeviceName, SND_PCM_STREAM_PLAYBACK, 0 ) < 0 ) + { + result = paBadIODeviceCombination; + goto error; + } + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( GetAvailableFormats(stream->pcm_playback), + outputSampleFormat ); + stream->playback_hostsampleformat = hostOutputSampleFormat; + } + + + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + numInputChannels, inputSampleFormat, hostInputSampleFormat, + numOutputChannels, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, framesPerHostBuffer, + paUtilFixedHostBufferSize, callback, userData ); + if( result != paNoError ) + goto error; + + /* configure the streams */ + + if( numInputChannels > 0 ) + { + int interleaved; + PaSampleFormat plain_format = hostInputSampleFormat & ~paNonInterleaved; + + if( inputSampleFormat & paNonInterleaved ) + interleaved = 0; + else + interleaved = 1; + + if( ConfigureStream( stream->pcm_capture, numInputChannels, interleaved, + sampleRate, plain_format, framesPerHostBuffer ) != 0 ) + { + result = paBadIODeviceCombination; + goto error; + } + + stream->capture_interleaved = interleaved; + } + + if( numOutputChannels > 0 ) + { + int interleaved; + PaSampleFormat plain_format = hostOutputSampleFormat & ~paNonInterleaved; + + if( outputSampleFormat & paNonInterleaved ) + interleaved = 0; + else + interleaved = 1; + + if( ConfigureStream( stream->pcm_playback, numOutputChannels, interleaved, + sampleRate, plain_format, framesPerHostBuffer ) != 0 ) + { + result = paBadIODeviceCombination; + goto error; + } + + stream->playback_interleaved = interleaved; + } + + stream->capture_nfds = 0; + stream->playback_nfds = 0; + + if( stream->pcm_capture ) + stream->capture_nfds = snd_pcm_poll_descriptors_count( stream->pcm_capture ); + + if( stream->pcm_playback ) + stream->playback_nfds = snd_pcm_poll_descriptors_count( stream->pcm_playback ); + + /* TODO: free this properly */ + printf("trying to allocate %d bytes of memory\n", (stream->capture_nfds + stream->playback_nfds + 1) * sizeof(struct pollfd) ); + stream->pfds = (struct pollfd*)PaUtil_AllocateMemory( (stream->capture_nfds + + stream->playback_nfds + 1) * + sizeof(struct pollfd) ); + if( !stream->pfds ) + { + printf("bad memory point 1\n"); + result = paInsufficientMemory; + goto error; + } + + stream->frames_per_period = framesPerHostBuffer; + stream->capture_channels = numInputChannels; + stream->playback_channels = numOutputChannels; + + *s = (PaStream*)stream; + + return result; + +error: + if( stream ) + PaUtil_FreeMemory( stream ); + + 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; + PaAlsaStream *stream = (PaAlsaStream*)s; + + if( stream->pcm_capture ) + { + snd_pcm_close( stream->pcm_capture ); + } + + if( stream->pcm_playback ) + { + snd_pcm_close( stream->pcm_playback ); + } + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + + return result; +} + + +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaAlsaStream *stream = (PaAlsaStream*)s; + + /* TODO: support errorText */ +#define ENSURE(x) \ + { \ + int error_ret; \ + error_ret = (x); \ + if( error_ret != 0 ) { \ + PaHostErrorInfo err; \ + err.errorCode = error_ret; \ + err.hostApiType = paALSA; \ + printf("call at %d failed\n", __LINE__); \ + return paUnanticipatedHostError; \ + } \ + else \ + printf("call at line %d succeeded\n", __LINE__); \ + } + + if( stream->pcm_capture ) + { + ENSURE( snd_pcm_prepare( stream->pcm_capture ) ); + } + + if( stream->pcm_playback ) + { + const snd_pcm_channel_area_t *playback_areas, *area; + snd_pcm_uframes_t offset, frames; + int sample_size = Pa_GetSampleSize( stream->playback_hostsampleformat ); + printf("Sample size: %d\n", sample_size ); + ENSURE( snd_pcm_prepare( stream->pcm_playback ) ); + frames = snd_pcm_avail_update( stream->pcm_playback ); + printf("frames: %d\n", (int)frames ); + printf("channels: %d\n", stream->playback_channels ); + + snd_pcm_mmap_begin( stream->pcm_playback, &playback_areas, &offset, &frames ); + + /* Insert silence */ + if( stream->playback_interleaved ) + { + void *playback_buffer; + area = &playback_areas[0]; + playback_buffer = area->addr + (area->first + area->step * offset) / 8; + memset( playback_buffer, 0, + frames * stream->playback_channels * sample_size ); + } + else + { + int i; + for( i = 0; i < stream->playback_channels; i++ ) + { + void *channel_buffer; + area = &playback_areas[i]; + channel_buffer = area->addr + (area->first + area->step * offset) / 8; + memset( channel_buffer, 0, frames * sample_size ); + } + } + + snd_pcm_mmap_commit( stream->pcm_playback, offset, frames ); + } + + if( stream->callback_mode ) + { + ENSURE( pthread_create( &stream->callback_thread, NULL, &CallbackThread, stream ) ); + + /* we'll do the snd_pcm_start() in the callback thread */ + } + else + { + if( stream->pcm_capture ) + snd_pcm_start( stream->pcm_capture ); + if( stream->pcm_playback ) + snd_pcm_start( stream->pcm_playback ); + } + + /* On my machine, the pcm stream will not transition to the RUNNING + * state for a while after snd_pcm_start is called. The PortAudio + * client needs to be able to depend on Pa_IsStreamActive() returning + * true the second after this function returns. So I sleep briefly here. + * + * I don't like this one bit. + */ + Pa_Sleep( 100 ); + + stream->callback_finished = 0; + + return result; +} + + +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaAlsaStream *stream = (PaAlsaStream*)s; + + /* First deal with the callback thread, cancelling and/or joining + * it if necessary + */ + + if( stream->callback_mode && stream->callback_finished ) + { + /* We are running in callback mode but the callback thread has + * already been cancelled by the return value from the user's + * callback function. Therefore we don't need to cancel the + * thread, but we do want to wait for it. */ + pthread_join( stream->callback_thread, NULL ); + } + else if( stream->callback_mode ) + { + /* We are running in callback mode, and the callback thread + * is still running. Cancel it and wait for it to be done. */ + pthread_cancel( stream->callback_thread ); + pthread_join( stream->callback_thread, NULL ); + } + + /* Stop the ALSA streams if necessary */ + + if( stream->callback_mode && stream->callback_finished ) + { + /* If we are in the callback_finished state the callback thread + * already stopped the streams. So there is nothing to do here. + */ + } + else + { + if( stream->pcm_capture ) + { + snd_pcm_drain( stream->pcm_capture ); + } + + if( stream->pcm_playback ) + { + snd_pcm_drain( stream->pcm_playback ); + } + } + + stream->callback_finished = 0; + + return result; +} + + +static PaError AbortStream( PaStream *s ) +{ + PaError result = paNoError; + PaAlsaStream *stream = (PaAlsaStream*)s; + + /* First deal with the callback thread, cancelling and/or joining + * it if necessary + */ + + if( stream->callback_mode && stream->callback_finished ) + { + /* We are running in callback mode but the callback thread has + * already been cancelled by the return value from the user's + * callback function. Therefore we don't need to cancel the + * thread, but we do want to wait for it. */ + pthread_join( stream->callback_thread, NULL ); + } + else if( stream->callback_mode ) + { + /* We are running in callback mode, and the callback thread + * is still running. Cancel it and wait for it to be done. */ + pthread_cancel( stream->callback_thread ); + pthread_join( stream->callback_thread, NULL ); + } + + /* Stop the ALSA streams if necessary */ + + if( stream->callback_mode && stream->callback_finished ) + { + /* If we are in the callback_finished state the callback thread + * already stopped the streams. So there is nothing to do here. + */ + } + else + { + if( stream->pcm_capture ) + { + snd_pcm_drop( stream->pcm_capture ); + } + + if( stream->pcm_playback ) + { + snd_pcm_drop( stream->pcm_playback ); + } + } + + stream->callback_finished = 0; + + return result; +} + + +static PaError IsStreamStopped( PaStream *s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + if( IsStreamActive(s) || stream->callback_finished ) + return 0; + else + return 1; +} + + +static PaError IsStreamActive( PaStream *s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + if( stream->pcm_capture ) + { + snd_pcm_state_t capture_state = snd_pcm_state( stream->pcm_capture ); + + if( capture_state == SND_PCM_STATE_RUNNING /*|| + capture_state == SND_PCM_STATE_PREPARED*/ ) + return 1; + } + + if( stream->pcm_playback ) + { + snd_pcm_state_t playback_state = snd_pcm_state( stream->pcm_playback ); + + if( playback_state == SND_PCM_STATE_RUNNING /*|| + playback_state == SND_PCM_STATE_PREPARED*/ ) + return 1; + } + + return 0; +} + + +static PaTime GetStreamTime( PaStream *s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + snd_output_t *output; + snd_timestamp_t timestamp; + snd_pcm_status_t *status; + snd_pcm_status_alloca( &status ); + + /* TODO: what if we have both? does it really matter? */ + + /* TODO: if running in callback mode, this will mean + * libasound routines are being called form multiple threads. + * need to verify that libasound is thread-safe. */ + + if( stream->pcm_capture ) + { + snd_pcm_status( stream->pcm_capture, status ); + } + else if( stream->pcm_playback ) + { + snd_pcm_status( stream->pcm_playback, status ); + } + + snd_pcm_status_get_tstamp( status, ×tamp ); + + return timestamp.tv_sec + ((float)timestamp.tv_usec/1000000); +} + + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaAlsaStream *stream = (PaAlsaStream*)s; + + return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); +} + diff --git a/pd/portaudio/pa_linux_alsa/pa_linux_alsa.h b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.h new file mode 100644 index 00000000..62c9512c --- /dev/null +++ b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.h @@ -0,0 +1,45 @@ + +#include + +#include + +#include "pa_util.h" +#include "pa_process.h" +#include "pa_cpuload.h" +#include "pa_stream.h" + +typedef struct PaAlsaStream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + snd_pcm_t *pcm_capture; + snd_pcm_t *pcm_playback; + + int callback_finished; /* bool: are we in the "callback finished" state? */ + + int frames_per_period; + int playback_hostsampleformat; + + int capture_channels; + int playback_channels; + + int capture_interleaved; /* bool: is capture interleaved? */ + int playback_interleaved; /* bool: is playback interleaved? */ + + int callback_mode; /* bool: are we running in callback mode? */ + pthread_t callback_thread; + + /* the callback thread uses these to poll the sound device, waiting + * for data to be ready/available */ + unsigned int capture_nfds; + unsigned int playback_nfds; + struct pollfd *pfds; + + /* these aren't really stream state, the callback uses them */ + snd_pcm_uframes_t capture_offset; + snd_pcm_uframes_t playback_offset; +} +PaAlsaStream; + diff --git a/pd/portaudio/pa_linux_alsa/pa_linux_alsa.o b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.o new file mode 100644 index 00000000..f79af61d Binary files /dev/null and b/pd/portaudio/pa_linux_alsa/pa_linux_alsa.o differ diff --git a/pd/portaudio/pa_mac_core/notes.txt b/pd/portaudio/pa_mac_core/notes.txt index 3b557d9a..c79b90e6 100644 --- a/pd/portaudio/pa_mac_core/notes.txt +++ b/pd/portaudio/pa_mac_core/notes.txt @@ -2,25 +2,25 @@ Notes on Core Audio Implementation of PortAudio by Phil Burk and Darren Gibbs -Document last updated October 18, 2002 +Document last updated March 20, 2002 WHAT WORKS Output with very low latency, <10 msec. Half duplex input or output. -Full duplex +Full duplex on the same CoreAudio device. The paFLoat32, paInt16, paInt8, paUInt8 sample formats. Pa_GetCPULoad() Pa_StreamTime() KNOWN BUGS OR LIMITATIONS -The iMic supports multiple sample rates. -But there is a bug when changing sample rates: - Run patest_record.c at rate A - it works. - Then run patest_record.c at rate B - it FAIL! - Then run patest_record.c again at rate B - it works! +We do not yet support simultaneous input and output on different +devices. Note that some CoreAudio devices like the Roland UH30 look +like one device but are actually two different CoreAudio devices. The +BuiltIn audio is typically one CoreAudio device. +Mono doesn't work. DEVICE MAPPING diff --git a/pd/portaudio/pa_mac_core/pa_mac_core.c b/pd/portaudio/pa_mac_core/pa_mac_core.c index de781d09..9a4b1488 100644 --- a/pd/portaudio/pa_mac_core/pa_mac_core.c +++ b/pd/portaudio/pa_mac_core/pa_mac_core.c @@ -1,5 +1,5 @@ /* - * $Id: pa_mac_core.c,v 1.8.4.3 2002/10/18 01:29:06 philburk Exp $ + * $Id: pa_mac_core.c,v 1.8 2002/04/12 18:07:20 philburk Exp $ * pa_mac_core.c * Implementation of PortAudio for Mac OS X Core Audio * @@ -7,18 +7,7 @@ * Latest Version at: http://www.portaudio.com * * Authors: Ross Bencina and Phil Burk - * Copyright (c) 1999-2002 Ross Bencina and Phil Burk - * - * Theory of Operation - * - * This code uses the HAL (Hardware Access Layer) of the Apple CoreAudio library. - * This is the layer closes to the hardware. - * The HAL layer only supports the native HW supported sample rates. - * So if the chip only supports 44100 Hz, then the HAL only supports 44100. - * To provide other rates we use the handy Apple AUConverter which provides - * sample rate conversion, mono-to-stereo conversion, and buffer size adaptation. - * - * License + * 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 @@ -54,28 +43,18 @@ audio input works if using same CoreAudio device (some HW devices make separate CoreAudio devices). 2.22.2002 - Stephane Letz - Explicit cast needed for compilation with Code Warrior 7 3.19.2002 - Phil Burk - Added paInt16, paInt8, format using new "pa_common/pa_convert.c" file. - Return error if opened in mono mode cuz not supported. [Supported 10.12.2002] + Return error if opened in mono mode cuz not supported. Add support for Pa_GetCPULoad(); Fixed timestamp in callback and Pa_StreamTime() (Thanks n++k for the advice!) Check for invalid sample rates and return an error. - Check for getenv("PA_MIN_LATENCY_MSEC") to set latency externally. + Check for getenv("PA_MIN_LATEWNCY_MSEC") to set latency externally. Better error checking for invalid channel counts and invalid devices. 3.29.2002 - Phil Burk - Fixed Pa_GetCPULoad() for small buffers. 3.31.2002 - Phil Burk - Use getrusage() instead of gettimeofday() for CPU Load calculation. - 10.12.2002 - Phil Burk - Use AudioConverter to allow wide range of sample rates, and mono. - Use FIFO (from pablio/rinbuffer.h) so that we can pull data through converter. - Added PaOSX_FixVolumeScalar() to make iMic audible. - 10.17.2002 - Phil Burk - Support full duplex between two different devices. - Name internal functions PaOSX_* - Dumped useless PA_MIN_LATENCY_MSEC environment variable. - Use kAudioDevicePropertyStreamFormatMatch to determine max channels. TODO: -O- debug problem when changing sample rates on iMic -O- add support for paInt32 format -O- Why does iMic have grunge for the first second or two then clears up? -O- request notification when formats change or device unplugged -O- Why does patest_wire.c on iMic chop up sound when SR=34567Hz? +O- how do mono output? +O- FIFO between input and output callbacks if different devices, like in pa_mac.c */ #include @@ -83,14 +62,10 @@ O- Why does patest_wire.c on iMic chop up sound when SR=34567Hz? #include #include #include -#include -#include -#include #include "portaudio.h" #include "pa_host.h" #include "pa_trace.h" -#include "ringbuffer.h" /************************************************* Constants ********/ @@ -98,45 +73,37 @@ O- Why does patest_wire.c on iMic chop up sound when SR=34567Hz? #define PA_TRACE_RUN (0) #define PA_TRACE_START_STOP (1) -#define PA_MIN_LATENCY_MSEC (1) +#define PA_MIN_LATENCY_MSEC (8) #define MIN_TIMEOUT_MSEC (1000) #define PRINT(x) { printf x; fflush(stdout); } -#define PRINT_ERR( msg, err ) PRINT(( msg ": error = 0x%0lX = '%s'\n", (err), ErrorToString(err)) ) -#define DBUG(x) /* PRINT(x) */ -#define DBUGBACK(x) /* if( sMaxBackgroundErrorMessages-- > 0 ) PRINT(x) */ -#define DBUGX(x) +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) /**/ +#define DBUGX(x) /* PRINT(x) /**/ // define value of isInput passed to CoreAudio routines #define IS_INPUT (true) #define IS_OUTPUT (false) -typedef struct PaHostInOut -{ - AudioDeviceID audioDeviceID; // CoreAudio specific ID - int bytesPerUserNativeBuffer; /* User buffer size in native host format. Depends on numChannels. */ - AudioConverterRef converter; - void *converterBuffer; -} PaHostInOut; - /************************************************************** * Structure for internal host specific stream data. * This is allocated on a per stream basis. */ typedef struct PaHostSoundControl { - PaHostInOut input; - PaHostInOut output; - AudioDeviceID primaryDeviceID; - Boolean usingSecondDevice; - int framesPerHostBuffer; - /* For sample rate, format conversion, or when using two devices. */ - RingBuffer ringBuffer; - char *ringBufferData; + AudioDeviceID pahsc_AudioDeviceID; // Must be the same for input and output for now. + /* Input -------------- */ + int pahsc_BytesPerUserNativeInputBuffer; /* native buffer size in bytes per user chunk */ + /* Output -------------- */ + int pahsc_BytesPerUserNativeOutputBuffer; /* native buffer size in bytes per user chunk */ + /* Init Time -------------- */ + int pahsc_FramesPerHostBuffer; + int pahsc_UserBuffersPerHostBuffer; /* For measuring CPU utilization. */ - struct rusage entryRusage; - double inverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ -} PaHostSoundControl; + struct rusage pahsc_EntryRusage; + double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ +} +PaHostSoundControl; /************************************************************** * Structure for internal extended device info. @@ -158,18 +125,14 @@ static int sNumOutputDevices = 0; static PaHostDeviceInfo *sDeviceInfos = NULL; static int sDefaultInputDeviceID = paNoDevice; static int sDefaultOutputDeviceID = paNoDevice; -static int sSavedHostError = 0; +static int sPaHostError = 0; + static int sNumCoreDevices = 0; static AudioDeviceID *sCoreDeviceIDs; // Array of Core AudioDeviceIDs -static const double supportedSampleRateRange[] = { 8000.0, 96000.0 }; static const char sMapperSuffixInput[] = " - Input"; static const char sMapperSuffixOutput[] = " - Output"; -/* Debug support. */ -//static int sMaxBackgroundErrorMessages = 100; -//static int sCoverageCounter = 1; // used to check code coverage during validation - /* We index the input devices first, then the output devices. */ #define LOWEST_INPUT_DEVID (0) #define HIGHEST_INPUT_DEVID (sNumInputDevices - 1) @@ -180,64 +143,15 @@ static const char sMapperSuffixOutput[] = " - Output"; /************************************************* Prototypes **********/ -static PaError PaOSX_QueryDevices( void ); -static int PaOSX_ScanDevices( Boolean isInput ); -static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ); -static PaDeviceID PaOSX_QueryDefaultInputDevice( void ); -static PaDeviceID PaOSX_QueryDefaultOutputDevice( void ); -static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past ); - -/**********************************************************************/ -/* OS X errors are 4 character ID that can be printed. - * Note that uses a static pad so result must be printed immediately. - */ -static OSStatus statusText[2] = { 0, 0 }; -static const char *ErrorToString( OSStatus err ) -{ - const char *str; - - switch (err) - { - case kAudioHardwareUnspecifiedError: - str = "kAudioHardwareUnspecifiedError"; - break; - case kAudioHardwareNotRunningError: - str = "kAudioHardwareNotRunningError"; - break; - case kAudioHardwareUnknownPropertyError: - str = "kAudioHardwareUnknownPropertyError"; - break; - case kAudioDeviceUnsupportedFormatError: - str = "kAudioDeviceUnsupportedFormatError"; - break; - case kAudioHardwareBadPropertySizeError: - str = "kAudioHardwareBadPropertySizeError"; - break; - case kAudioHardwareIllegalOperationError: - str = "kAudioHardwareIllegalOperationError"; - break; - default: - str = "Unknown CoreAudio Error!"; - statusText[0] = err; - str = (const char *)statusText; - break; - } +static PaError Pa_QueryDevices( void ); +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ); - return str; -} +static int PaHost_ScanDevices( Boolean isInput ); +static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ); -/**********************************************************************/ -static unsigned long RoundUpToNextPowerOf2( unsigned long n ) -{ - long numBits = 0; - if( ((n-1) & n) == 0) return n; /* Already Power of two. */ - while( n > 0 ) - { - n= n>>1; - numBits++; - } - return (1<past_DeviceData; if( pahsc == NULL ) return; /* Query user CPU timer for usage analysis and to prevent overuse of CPU. */ - getrusage( RUSAGE_SELF, &pahsc->entryRusage ); + getrusage( RUSAGE_SELF, &pahsc->pahsc_EntryRusage ); } static long SubtractTime_AminusB( struct timeval *timeA, struct timeval *timeB ) @@ -274,10 +188,10 @@ static void Pa_EndUsageCalculation( internalPortAudioStream *past ) if( getrusage( RUSAGE_SELF, ¤tRusage ) == 0 ) { - usecsElapsed = SubtractTime_AminusB( ¤tRusage.ru_utime, &pahsc->entryRusage.ru_utime ); + usecsElapsed = SubtractTime_AminusB( ¤tRusage.ru_utime, &pahsc->pahsc_EntryRusage.ru_utime ); /* Use inverse because it is faster than the divide. */ - newUsage = usecsElapsed * pahsc->inverseMicrosPerHostBuffer; + newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer; past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + (LOWPASS_COEFFICIENT_1 * newUsage); @@ -286,104 +200,92 @@ static void Pa_EndUsageCalculation( internalPortAudioStream *past ) /****************************************** END CPU UTILIZATION *******/ /************************************************************************/ -static PaDeviceID PaOSX_QueryDefaultInputDevice( void ) +static PaDeviceID Pa_QueryDefaultInputDevice( void ) { - OSStatus err = noErr; - UInt32 count; - int i; + OSStatus err = noErr; + UInt32 count; + int i; AudioDeviceID tempDeviceID = kAudioDeviceUnknown; - PaDeviceID defaultDeviceID = paNoDevice; + PaDeviceID defaultDeviceID = paNoDevice; // get the default output device for the HAL // it is required to pass the size of the data to be returned count = sizeof(tempDeviceID); err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultInputDevice, &count, (void *) &tempDeviceID); - if (err != noErr) goto error; + if (err != noErr) goto Bail; // scan input devices to see which one matches this device defaultDeviceID = paNoDevice; for( i=LOWEST_INPUT_DEVID; i<=HIGHEST_INPUT_DEVID; i++ ) { - DBUG(("PaOSX_QueryDefaultInputDevice: i = %d, aDevId = %ld\n", i, sDeviceInfos[i].audioDeviceID )); + DBUG(("Pa_QueryDefaultInputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID )); if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) { defaultDeviceID = i; break; } } -error: +Bail: return defaultDeviceID; } /************************************************************************/ -static PaDeviceID PaOSX_QueryDefaultOutputDevice( void ) +static PaDeviceID Pa_QueryDefaultOutputDevice( void ) { - OSStatus err = noErr; - UInt32 count; - int i; + OSStatus err = noErr; + UInt32 count; + int i; AudioDeviceID tempDeviceID = kAudioDeviceUnknown; - PaDeviceID defaultDeviceID = paNoDevice; + PaDeviceID defaultDeviceID = paNoDevice; // get the default output device for the HAL // it is required to pass the size of the data to be returned count = sizeof(tempDeviceID); err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &count, (void *) &tempDeviceID); - if (err != noErr) goto error; + if (err != noErr) goto Bail; // scan output devices to see which one matches this device defaultDeviceID = paNoDevice; for( i=LOWEST_OUTPUT_DEVID; i<=HIGHEST_OUTPUT_DEVID; i++ ) { - DBUG(("PaOSX_QueryDefaultOutputDevice: i = %d, aDevId = %ld\n", i, sDeviceInfos[i].audioDeviceID )); + DBUG(("Pa_QueryDefaultOutputDevice: i = %d, aDevId = %d\n", i, sDeviceInfos[i].audioDeviceID )); if( sDeviceInfos[i].audioDeviceID == tempDeviceID ) { defaultDeviceID = i; break; } } -error: +Bail: return defaultDeviceID; } /******************************************************************/ -static PaError PaOSX_QueryDevices( void ) +static PaError Pa_QueryDevices( void ) { OSStatus err = noErr; UInt32 outSize; Boolean outWritable; - int numBytes; + int numBytes; // find out how many Core Audio devices there are, if any - outSize = sizeof(outWritable); err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &outSize, &outWritable); if (err != noErr) - { - PRINT_ERR("Couldn't get info about list of audio devices", err); - sSavedHostError = err; - return paHostError; - } - + ERR_RPT(("Couldn't get info about list of audio devices\n")); + // calculate the number of device available sNumCoreDevices = outSize / sizeof(AudioDeviceID); // Bail if there aren't any devices if (sNumCoreDevices < 1) - { - PRINT(("No Devices Available")); - return paHostError; - } - + ERR_RPT(("No Devices Available\n")); + // make space for the devices we are about to get sCoreDeviceIDs = (AudioDeviceID *)malloc(outSize); // get an array of AudioDeviceIDs err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &outSize, (void *)sCoreDeviceIDs); if (err != noErr) - { - PRINT_ERR("Couldn't get list of audio device IDs", err); - sSavedHostError = err; - return paHostError; - } + ERR_RPT(("Couldn't get list of audio device IDs\n")); // Allocate structures to hold device info pointers. // There will be a maximum of two Pa devices per Core Audio device, input and/or output. @@ -393,22 +295,35 @@ static PaError PaOSX_QueryDevices( void ) // Scan all the Core Audio devices to see which support input and allocate a // PaHostDeviceInfo structure for each one. - PaOSX_ScanDevices( IS_INPUT ); + PaHost_ScanDevices( IS_INPUT ); sNumInputDevices = sNumPaDevices; // Now scan all the output devices. - PaOSX_ScanDevices( IS_OUTPUT ); + PaHost_ScanDevices( IS_OUTPUT ); sNumOutputDevices = sNumPaDevices - sNumInputDevices; // Figure out which of the devices that we scanned is the default device. - sDefaultInputDeviceID = PaOSX_QueryDefaultInputDevice(); - sDefaultOutputDeviceID = PaOSX_QueryDefaultOutputDevice(); + sDefaultInputDeviceID = Pa_QueryDefaultInputDevice(); + sDefaultOutputDeviceID = Pa_QueryDefaultOutputDevice(); return paNoError; } +/************************************************************************************/ +long Pa_GetHostError() +{ + return sPaHostError; +} + +/*************************************************************************/ +int Pa_CountDevices() +{ + if( sNumPaDevices <= 0 ) Pa_Initialize(); + return sNumPaDevices; +} + /*************************************************************************/ /* Allocate a string containing the device name. */ -static char *PaOSX_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) +static char *PaHost_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) { OSStatus err = noErr; UInt32 outSize; @@ -424,18 +339,87 @@ static char *PaOSX_DeviceNameFromID(AudioDeviceID deviceID, Boolean isInput ) { err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyDeviceName, &outSize, deviceName); if (err != noErr) - PRINT_ERR("Couldn't get audio device name", err); + ERR_RPT(("Couldn't get audio device name.\n")); } } return deviceName; } +/*************************************************************************/ +// An AudioStreamBasicDescription is passed in to query whether or not +// the format is supported. A kAudioDeviceUnsupportedFormatError will +// be returned if the format is not supported and kAudioHardwareNoError +// will be returned if it is supported. AudioStreamBasicDescription +// fields set to 0 will be ignored in the query, but otherwise values +// must match exactly. + +Boolean deviceDoesSupportFormat(AudioDeviceID deviceID, AudioStreamBasicDescription *desc, Boolean isInput ) +{ + OSStatus err = noErr; + UInt32 outSize; + + outSize = sizeof(*desc); + err = AudioDeviceGetProperty(deviceID, 0, isInput, kAudioDevicePropertyStreamFormatSupported, &outSize, desc); + + if (err == kAudioHardwareNoError) + return true; + else + return false; +} + +/*************************************************************************/ +// return an error string +char* coreAudioErrorString (int errCode ) +{ + char *str; + + switch (errCode) + { + case kAudioHardwareUnspecifiedError: + str = "kAudioHardwareUnspecifiedError"; + break; + case kAudioHardwareNotRunningError: + str = "kAudioHardwareNotRunningError"; + break; + case kAudioHardwareUnknownPropertyError: + str = "kAudioHardwareUnknownPropertyError"; + break; + case kAudioDeviceUnsupportedFormatError: + str = "kAudioDeviceUnsupportedFormatError"; + break; + case kAudioHardwareBadPropertySizeError: + str = "kAudioHardwareBadPropertySizeError"; + break; + case kAudioHardwareIllegalOperationError: + str = "kAudioHardwareIllegalOperationError"; + break; + default: + str = "Unknown CoreAudio Error!"; + break; + } + + return str; +} + +/************************************************************************* +** PaDeviceInfo structures have already been created +** so just return the pointer. +** +*/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + if( id < 0 || id >= sNumPaDevices ) + return NULL; + + return &sDeviceInfos[id].paInfo; +} + /************************************************************************* ** Scan all of the Core Audio devices to see which support input or output. ** Changes sNumDevices, and fills in sDeviceInfos. */ -static int PaOSX_ScanDevices( Boolean isInput ) +static int PaHost_ScanDevices( Boolean isInput ) { int coreDeviceIndex; int result; @@ -446,8 +430,8 @@ static int PaOSX_ScanDevices( Boolean isInput ) { // try to fill in next PaHostDeviceInfo hostDeviceInfo = &sDeviceInfos[sNumPaDevices]; - result = PaOSX_QueryDeviceInfo( hostDeviceInfo, coreDeviceIndex, isInput ); - DBUGX(("PaOSX_ScanDevices: paDevId = %d, coreDevId = %d\n", sNumPaDevices, coreDeviceIndex )); + result = PaHost_QueryDeviceInfo( hostDeviceInfo, coreDeviceIndex, isInput ); + DBUG(("PaHost_ScanDevices: paDevId = %d, coreDevId = %d\n", sNumPaDevices, hostDeviceInfo->audioDeviceID )); if( result > 0 ) { sNumPaDevices += 1; // bump global counter if we got one @@ -458,7 +442,6 @@ static int PaOSX_ScanDevices( Boolean isInput ) return numAdded; } - /************************************************************************* ** Try to fill in the device info for this device. ** Return 1 if a good device that PA can use. @@ -466,55 +449,59 @@ static int PaOSX_ScanDevices( Boolean isInput ) ** or return negative error. ** */ -static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ) +static int PaHost_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDeviceIndex, Boolean isInput ) { - OSStatus err; - UInt32 outSize; + OSErr err; + int index; + UInt32 outSize; AudioStreamBasicDescription formatDesc; + Boolean result; AudioDeviceID devID; - PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo; + double *sampleRates = NULL; /* non-const ptr */ + + PaDeviceInfo *deviceInfo = &hostDeviceInfo->paInfo; + double possibleSampleRates[] = {8000.0, 11025.0, 22050.0, 44100.0, 48000.0, 88200.0, 96000.0}; + int maxNumSampleRates = sizeof( possibleSampleRates ) / sizeof( double ); deviceInfo->structVersion = 1; deviceInfo->maxInputChannels = 0; deviceInfo->maxOutputChannels = 0; - - deviceInfo->sampleRates = supportedSampleRateRange; // because we use sample rate converter to get continuous rates deviceInfo->numSampleRates = -1; devID = sCoreDeviceIDs[ coreDeviceIndex ]; hostDeviceInfo->audioDeviceID = devID; - DBUG(("PaOSX_QueryDeviceInfo: coreDeviceIndex = %d, devID = %d, isInput = %d\n", - coreDeviceIndex, devID, isInput )); - // Get data format info from the device. - outSize = sizeof(formatDesc); - err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc); - // This just may not be an appropriate device for input or output so leave quietly. - if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error; - // Right now the Core Audio headers only define one formatID: LinearPCM - // Apparently LinearPCM must be Float32 for now. - if( (formatDesc.mFormatID == kAudioFormatLinearPCM) && - (formatDesc.mFormatFlags & kLinearPCMFormatFlagIsFloat) ) - { - deviceInfo->nativeSampleFormats = paFloat32; - } - else + // Figure out supported sample rates + // Make room in case device supports all rates. + sampleRates = (double*)PaHost_AllocateFastMemory( maxNumSampleRates * sizeof(double) ); + if( sampleRates == NULL ) return paInsufficientMemory; + + deviceInfo->sampleRates = sampleRates; + deviceInfo->numSampleRates = 0; + + // Loop through the possible sampling rates and check each to see if the device supports it. + for (index = 0; index < maxNumSampleRates; index ++) { - return paSampleFormatNotSupported; + memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); + formatDesc.mSampleRate = possibleSampleRates[index]; + result = deviceDoesSupportFormat( devID, &formatDesc, isInput ); + + if (result == true) + { + deviceInfo->numSampleRates += 1; + *sampleRates = possibleSampleRates[index]; + sampleRates++; + } } + // If no sample rates supported, then not a very good device. + if( deviceInfo->numSampleRates == 0 ) goto error; - // Determine maximum number of channels supported. - memset( &formatDesc, 0, sizeof(formatDesc)); - formatDesc.mChannelsPerFrame = 256; // FIXME - what about device with > 256 channels + // Get data format info from the device. outSize = sizeof(formatDesc); - err = AudioDeviceGetProperty( devID, 0, - isInput, kAudioDevicePropertyStreamFormatMatch, &outSize, &formatDesc); - if( err != noErr ) - { - PRINT_ERR("PaOSX_QueryDeviceInfo: Could not get device format match", err); - sSavedHostError = err; - return paHostError; - } + err = AudioDeviceGetProperty(devID, 0, isInput, kAudioDevicePropertyStreamFormat, &outSize, &formatDesc); + + // If no channels supported, then not a very good device. + if( (err != noErr) || (formatDesc.mChannelsPerFrame == 0) ) goto error; if( isInput ) { @@ -525,268 +512,205 @@ static int PaOSX_QueryDeviceInfo( PaHostDeviceInfo *hostDeviceInfo, int coreDevi deviceInfo->maxOutputChannels = formatDesc.mChannelsPerFrame; } + // FIXME - where to put current sample rate?: formatDesc.mSampleRate + + // Right now the Core Audio headers only define one formatID: LinearPCM + // Apparently LinearPCM must be Float32 for now. + switch (formatDesc.mFormatID) + { + case kAudioFormatLinearPCM: + deviceInfo->nativeSampleFormats = paFloat32; + + // FIXME - details about the format are in these flags. + // formatDesc.mFormatFlags + + // here are the possibilities + // kLinearPCMFormatFlagIsFloat // set for floating point, clear for integer + // kLinearPCMFormatFlagIsBigEndian // set for big endian, clear for little + // kLinearPCMFormatFlagIsSignedInteger // set for signed integer, clear for unsigned integer, + // only valid if kLinearPCMFormatFlagIsFloat is clear + // kLinearPCMFormatFlagIsPacked // set if the sample bits are packed as closely together as possible, + // clear if they are high or low aligned within the channel + // kLinearPCMFormatFlagIsAlignedHigh // set if the sample bits are placed + break; + + default: + deviceInfo->nativeSampleFormats = paFloat32; // FIXME + break; + } + // Get the device name - deviceInfo->name = PaOSX_DeviceNameFromID( devID, isInput ); + deviceInfo->name = PaHost_DeviceNameFromID( devID, isInput ); return 1; error: + if( sampleRates != NULL ) free( sampleRates ); return 0; } -/**********************************************************************/ -static PaError PaOSX_MaybeQueryDevices( void ) +/************************************************************************* +** Returns recommended device ID. +** On the PC, the recommended device can be specified by the user by +** setting an environment variable. For example, to use device #1. +** +** set PA_RECOMMENDED_OUTPUT_DEVICE=1 +** +** The user should first determine the available device ID by using +** the supplied application "pa_devs". +*/ +#define PA_ENV_BUF_SIZE (32) +#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE") +#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE") + +static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName ) +{ +#if 0 + UInt32 hresult; + char envbuf[PA_ENV_BUF_SIZE]; + PaDeviceID recommendedID = paNoDevice; + + /* Let user determine default device by setting environment variable. */ + hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + recommendedID = atoi( envbuf ); + } + + return recommendedID; +#endif + return paNoDevice; +} + +static PaError Pa_MaybeQueryDevices( void ) { if( sNumPaDevices == 0 ) { - return PaOSX_QueryDevices(); + return Pa_QueryDevices(); } return 0; } -static char zeroPad[256] = { 0 }; - /********************************************************************** -** This is the proc that supplies the data to the AudioConverterFillBuffer call. -** We can pass back arbitrarily sized blocks so if the FIFO region is split -** just pass back the first half. +** Check for environment variable, else query devices and use result. */ -static OSStatus PaOSX_InputConverterCallbackProc (AudioConverterRef inAudioConverter, - UInt32* outDataSize, - void** outData, - void* inUserData) +PaDeviceID Pa_GetDefaultInputDeviceID( void ) { - internalPortAudioStream *past = (internalPortAudioStream *) inUserData; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - void *dataPtr1; - long size1; - void *dataPtr2; - long size2; - - /* Pass contiguous region from FIFO directly to converter. */ - RingBuffer_GetReadRegions( &pahsc->ringBuffer, *outDataSize, - &dataPtr1, &size1, &dataPtr2, &size2 ); - - if( size1 > 0 ) - { - *outData = dataPtr1; - *outDataSize = size1; - RingBuffer_AdvanceReadIndex( &pahsc->ringBuffer, size1 ); - DBUGX(("PaOSX_InputConverterCallbackProc: read %ld bytes from FIFO.\n", size1 )); - } - else + PaError result; + result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME ); + if( result < 0 ) { - DBUGBACK(("PaOSX_InputConverterCallbackProc: got no data!\n")); - *outData = zeroPad; // Give it zero data to keep it happy. - *outDataSize = sizeof(zeroPad); + result = Pa_MaybeQueryDevices(); + if( result < 0 ) return result; + result = sDefaultInputDeviceID; } - return noErr; + return result; } -/***************************************************************************** -** Get audio input, if any, from passed in buffer, or from converter or from FIFO -** and run PA callback. -*/ -static OSStatus PaOSX_LoadAndProcess( internalPortAudioStream *past, - void *inputBuffer, void *outputBuffer ) +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) { - OSStatus err = noErr; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - - if( past->past_StopSoon ) + PaError result; + result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME ); + if( result < 0 ) { - if( outputBuffer ) - { - /* Clear remainder of audio buffer if we are waiting for stop. */ - AddTraceMessage("PaOSX_HandleInputOutput: zero rest of wave buffer ", i ); - memset( outputBuffer, 0, pahsc->output.bytesPerUserNativeBuffer ); - } - } - else - { - /* Do we need data from the converted input? */ - if( pahsc->input.converter != NULL ) - { - UInt32 size = pahsc->input.bytesPerUserNativeBuffer; - err = AudioConverterFillBuffer( - pahsc->input.converter, - PaOSX_InputConverterCallbackProc, - past, - &size, - pahsc->input.converterBuffer); - if( err != noErr ) return err; - inputBuffer = pahsc->input.converterBuffer; - } - /* Or should just get the data directly from the FIFO? */ - else if( pahsc->ringBufferData != NULL ) - { - if( RingBuffer_GetReadAvailable( &pahsc->ringBuffer ) >= pahsc->input.bytesPerUserNativeBuffer) - { - RingBuffer_Read( &pahsc->ringBuffer, pahsc->input.converterBuffer, pahsc->input.bytesPerUserNativeBuffer ); - inputBuffer = pahsc->input.converterBuffer; - } - } - - /* Fill part of audio converter buffer by converting input to user format, - * calling user callback, then converting output to native format. */ - if( PaConvert_Process( past, inputBuffer, outputBuffer )) - { - past->past_StopSoon = 1; - } + result = Pa_MaybeQueryDevices(); + if( result < 0 ) return result; + result = sDefaultOutputDeviceID; } - return err; + return result; } -/***************************************************************************** -** This is the proc that supplies the data to the AudioConverterFillBuffer call +/********************************************************************** +** Initialize Host dependant part of API. */ -static OSStatus PaOSX_OutputConverterCallbackProc (AudioConverterRef inAudioConverter, - UInt32* outDataSize, - void** outData, - void* inUserData) + +PaError PaHost_Init( void ) { - internalPortAudioStream *past = (internalPortAudioStream *) inUserData; - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - - *outData = pahsc->output.converterBuffer; - *outDataSize = pahsc->output.bytesPerUserNativeBuffer; - - return PaOSX_LoadAndProcess ( past, pahsc->input.converterBuffer, pahsc->output.converterBuffer ); + return Pa_MaybeQueryDevices(); } /********************************************************************** ** Fill any available output buffers and use any available ** input buffers by calling user callback. -** Will set past->past_StopSoon if user callback indicates that it is finished. */ -static OSStatus PaOSX_HandleInputOutput( internalPortAudioStream *past, - const AudioBufferList* inInputData, - AudioBufferList* outOutputData ) +static PaError Pa_TimeSlice( internalPortAudioStream *past, const AudioBufferList* inInputData, + AudioBufferList* outOutputData ) { - OSStatus err = noErr; - char *inputNativeBufferfPtr = NULL; - char *outputNativeBufferfPtr = NULL; - int i; + PaError result = 0; + char *inputNativeBufferfPtr = NULL; + char *outputNativeBufferfPtr = NULL; + int i; + int buffersProcessed = 0; + int done = 0; PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + + past->past_NumCallbacks += 1; + +#if PA_TRACE_RUN + AddTraceMessage("Pa_TimeSlice: past_NumCallbacks ", past->past_NumCallbacks ); +#endif + + Pa_StartUsageCalculation( past ); /* If we are using output, then we need an empty output buffer. */ - if( outOutputData->mNumberBuffers > 0 ) + if( past->past_NumOutputChannels > 0 ) { outputNativeBufferfPtr = (char*)outOutputData->mBuffers[0].mData; } - if( inInputData->mNumberBuffers > 0 ) + /* If we are using input, then we need a full input buffer. */ + if( past->past_NumInputChannels > 0 ) { inputNativeBufferfPtr = (char*)inInputData->mBuffers[0].mData; - - /* If there is a FIFO for input then write to it. */ - if( (pahsc->ringBufferData != NULL) && !pahsc->usingSecondDevice ) - { - long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); - long numBytes = inInputData->mBuffers[0].mDataByteSize; - if( numBytes <= writeRoom ) - { - RingBuffer_Write( &pahsc->ringBuffer, inputNativeBufferfPtr, numBytes ); - DBUGBACK(("PaOSX_HandleInputOutput: wrote %ld bytes to FIFO.\n", inInputData->mBuffers[0].mDataByteSize)); - } // FIXME else ??? - } - } - - if( pahsc->output.converter != NULL ) - { - /* Using output and input converter. */ - UInt32 size = outOutputData->mBuffers[0].mDataByteSize; - err = AudioConverterFillBuffer( - pahsc->output.converter, - PaOSX_OutputConverterCallbackProc, - past, - &size, - outputNativeBufferfPtr); - if( err != noErr ) - { - PRINT_ERR("PaOSX_HandleInputOutput: AudioConverterFillBuffer failed", err); - goto error; - } - } - else if( (pahsc->input.converter != NULL) && !pahsc->usingSecondDevice) - { - /* Using just an input converter. */ - /* Generate user buffers as long as we have a half full input FIFO. */ - long gotHalf = pahsc->ringBuffer.bufferSize / 2; - while( (RingBuffer_GetReadAvailable( &pahsc->ringBuffer ) >= gotHalf) && - (past->past_StopSoon == 0) ) - { - err = PaOSX_LoadAndProcess ( past, NULL, outputNativeBufferfPtr ); - if( err != noErr ) goto error; - if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->output.bytesPerUserNativeBuffer; - } - } - else - { - /* No AUConverters used. */ - /* Each host buffer contains multiple user buffers so do them all now. */ - for( i=0; ipast_NumUserBuffers; i++ ) - { - err = PaOSX_LoadAndProcess ( past, inputNativeBufferfPtr, outputNativeBufferfPtr ); - if( err != noErr ) goto error; - if( inputNativeBufferfPtr ) inputNativeBufferfPtr += pahsc->input.bytesPerUserNativeBuffer; - if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->output.bytesPerUserNativeBuffer; - } } - -error: - return err; -} -/****************************************************************** - * This callback is used when two separate devices are used for input and output. - * This often happens when using USB devices which present as two devices: input and output. - * It just writes its data to a FIFO so that it can be read by the main callback - * proc PaOSX_CoreAudioIOCallback(). - */ -static OSStatus PaOSX_CoreAudioInputCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, - const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, - AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, - void* contextPtr) -{ - internalPortAudioStream *past = (internalPortAudioStream *) contextPtr; - PaHostSoundControl *pahsc; - pahsc = (PaHostSoundControl *) past->past_DeviceData; - - /* If there is a FIFO for input then write to it. */ - if( pahsc->ringBufferData != NULL ) + buffersProcessed += 1; + + /* Each host buffer contains multiple user buffers so do them all now. */ + for( i=0; ipahsc_UserBuffersPerHostBuffer; i++ ) { - long writeRoom = RingBuffer_GetWriteAvailable( &pahsc->ringBuffer ); - long numBytes = inInputData->mBuffers[0].mDataByteSize; - if( numBytes <= writeRoom ) + if( done ) { - RingBuffer_Write( &pahsc->ringBuffer, inInputData->mBuffers[0].mData, inInputData->mBuffers[0].mDataByteSize ); + if( outputNativeBufferfPtr ) + { + /* Clear remainder of wave buffer if we are waiting for stop. */ + AddTraceMessage("Pa_TimeSlice: zero rest of wave buffer ", i ); + memset( outputNativeBufferfPtr, 0, pahsc->pahsc_BytesPerUserNativeOutputBuffer ); + } } else { - DBUGBACK(("PaOSX_CoreAudioInputCallback: FIFO too full to write!\n")); - } + /* Convert 32 bit native data to user data and call user routine. */ + result = PaConvert_Process( past, inputNativeBufferfPtr, outputNativeBufferfPtr ); + if( result != 0) done = 1; + } + if( inputNativeBufferfPtr ) inputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeInputBuffer; + if( outputNativeBufferfPtr) outputNativeBufferfPtr += pahsc->pahsc_BytesPerUserNativeOutputBuffer; } - - return noErr; + + Pa_EndUsageCalculation( past ); + +#if PA_TRACE_RUN + AddTraceMessage("Pa_TimeSlice: buffersProcessed ", buffersProcessed ); +#endif + + return (result != 0) ? result : done; } -/****************************************************************** - * This is the primary callback for CoreAudio. - * It can handle input and/or output for a single device. - * It takes input from CoreAudio, converts it and passes it to the - * PortAudio callback. Then takes the PA results and passes it back to CoreAudio. - */ -static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioTimeStamp* inNow, +OSStatus appIOProc (AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* contextPtr) { - OSStatus err = noErr; + + PaError result = 0; internalPortAudioStream *past; PaHostSoundControl *pahsc; past = (internalPortAudioStream *) contextPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; +// printf("Num input Buffers: %d; Num output Buffers: %d.\n", inInputData->mNumberBuffers, outOutputData->mNumberBuffers); + /* Has someone asked us to abort by calling Pa_AbortStream()? */ if( past->past_StopNow ) { @@ -797,7 +721,7 @@ static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioT */ else if( past->past_StopSoon ) { - // FIXME - Pretend all done. Should wait for audio to play out but CoreAudio latency very low. + // FIXME - pretend all done past->past_IsActive = 0; /* Will cause thread to return. */ } else @@ -808,220 +732,63 @@ static OSStatus PaOSX_CoreAudioIOCallback (AudioDeviceID inDevice, const AudioT past->past_FrameCount = inOutputTime->mSampleTime; } - /* Measure CPU load. */ - Pa_StartUsageCalculation( past ); - past->past_NumCallbacks += 1; - /* Process full input buffer and fill up empty output buffers. */ - err = PaOSX_HandleInputOutput( past, inInputData, outOutputData ); - - Pa_EndUsageCalculation( past ); - } + if( (result = Pa_TimeSlice( past, inInputData, outOutputData )) != 0) + { + /* User callback has asked us to stop. */ +#if PA_TRACE_START_STOP + AddTraceMessage( "Pa_OutputThreadProc: TimeSlice() returned ", result ); +#endif + past->past_StopSoon = 1; /* Request that audio play out then stop. */ + result = paNoError; + } + } - // FIXME PaOSX_UpdateStreamTime( pahsc ); - if( err != 0 ) DBUG(("PaOSX_CoreAudioIOCallback: returns %ld.\n", err )); + // FIXME PaHost_UpdateStreamTime( pahsc ); - return err; + return result; } +#if 0 +static int PaHost_CalcTimeOut( internalPortAudioStream *past ) +{ + /* Calculate timeOut longer than longest time it could take to play all buffers. */ + int timeOut = (UInt32) (1500.0 * PaHost_GetTotalBufferFrames( past ) / past->past_SampleRate); + if( timeOut < MIN_TIMEOUT_MSEC ) timeOut = MIN_TIMEOUT_MSEC; + return timeOut; +} +#endif + + /*******************************************************************/ /* Attempt to set device sample rate. */ -static PaError PaOSX_SetSampleRate( AudioDeviceID devID, Boolean isInput, double sampleRate ) +static PaError PaHost_SetSampleRate( AudioDeviceID devID, Boolean isInput, double sampleRate ) { AudioStreamBasicDescription formatDesc; - PaError result = paNoError; OSStatus err; - UInt32 dataSize; - - // try to set to desired rate memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mSampleRate = sampleRate; err = AudioDeviceSetProperty( devID, 0, 0, isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); - if (err != noErr) - { - result = paInvalidSampleRate; - - /* Could not set to desired rate so query for closest match. */ - DBUG(("PaOSX_SetSampleRate: couldn't set to %f. Try to find match.\n", sampleRate )); - dataSize = sizeof(formatDesc); - AudioDeviceGetProperty( devID, 0, - isInput, kAudioDevicePropertyStreamFormatMatch, &dataSize, &formatDesc); - formatDesc.mSampleRate = sampleRate; - err = AudioDeviceGetProperty( devID, 0, - isInput, kAudioDevicePropertyStreamFormatMatch, &dataSize, &formatDesc); - if (err == noErr) - { - /* Set to that matching rate. */ - sampleRate = formatDesc.mSampleRate; - DBUG(("PaOSX_SetSampleRate: match succeeded, set to %f instead\n", sampleRate )); - memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); - formatDesc.mSampleRate = sampleRate; - AudioDeviceSetProperty( devID, 0, 0, - isInput, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); - } - } - return result; -} - -/******************************************************************* - * Check volume level of device. If below threshold, then set to newLevel. - * Using volume instead of decibels because decibel range varies by device. - */ -static void PaOSX_FixVolumeScalars( AudioDeviceID devID, Boolean isInput, - int numChannels, double threshold, double newLevel ) -{ - OSStatus err = noErr; - UInt32 dataSize; - int iChannel; - -/* The master channel is 0. Left and right are channels 1 and 2. */ -/* Fix volume. */ - for( iChannel = 0; iChannel<=numChannels; iChannel++ ) - { - Float32 fdata32; - dataSize = sizeof( fdata32 ); - err = AudioDeviceGetProperty( devID, iChannel, isInput, - kAudioDevicePropertyVolumeScalar, &dataSize, &fdata32 ); - if( err == noErr ) - { - DBUG(("kAudioDevicePropertyVolumeScalar for channel %d = %f\n", iChannel, fdata32)); - if( fdata32 <= (Float32) threshold ) - { - dataSize = sizeof( fdata32 ); - fdata32 = (Float32) newLevel; - err = AudioDeviceSetProperty( devID, 0, iChannel, isInput, - kAudioDevicePropertyVolumeScalar, dataSize, &fdata32 ); - if( err != noErr ) - { - PRINT(("Warning: audio volume is very low and could not be turned up.\n")); - } - else - { - PRINT(("Volume for audio channel %d was <= %4.2f so set to %4.2f by PortAudio!\n", - iChannel, threshold, newLevel )); - } - } - } - } -/* Unmute if muted. */ - for( iChannel = 0; iChannel<=numChannels; iChannel++ ) - { - UInt32 uidata32; - dataSize = sizeof( uidata32 ); - err = AudioDeviceGetProperty( devID, iChannel, isInput, - kAudioDevicePropertyMute, &dataSize, &uidata32 ); - if( err == noErr ) - { - DBUG(("uidata32 for channel %d = %ld\n", iChannel, uidata32)); - if( uidata32 == 1 ) // muted? - { - dataSize = sizeof( uidata32 ); - uidata32 = 0; // unmute - err = AudioDeviceSetProperty( devID, 0, iChannel, isInput, - kAudioDevicePropertyMute, dataSize, &uidata32 ); - if( err != noErr ) - { - PRINT(("Warning: audio is muted and could not be unmuted!\n")); - } - else - { - PRINT(("Audio channel %d was unmuted by PortAudio!\n", iChannel )); - } - } - } - } + if (err != kAudioHardwareNoError) return paInvalidSampleRate; + else return paNoError; } -#if 0 -static void PaOSX_DumpDeviceInfo( AudioDeviceID devID, Boolean isInput ) -{ - OSStatus err = noErr; - UInt32 dataSize; - UInt32 uidata32; - Float32 fdata32; - AudioValueRange audioRange; - - dataSize = sizeof( uidata32 ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyLatency, &dataSize, &uidata32 ); - if( err != noErr ) - { - PRINT_ERR("Error reading kAudioDevicePropertyLatency", err); - return; - } - PRINT(("kAudioDevicePropertyLatency = %d\n", (int)uidata32 )); - - dataSize = sizeof( fdata32 ); - err = AudioDeviceGetProperty( devID, 1, isInput, - kAudioDevicePropertyVolumeScalar, &dataSize, &fdata32 ); - if( err != noErr ) - { - PRINT_ERR("Error reading kAudioDevicePropertyVolumeScalar", err); - return; - } - PRINT(("kAudioDevicePropertyVolumeScalar = %f\n", fdata32 )); - - dataSize = sizeof( uidata32 ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyBufferSize, &dataSize, &uidata32 ); - if( err != noErr ) - { - PRINT_ERR("Error reading buffer size", err); - return; - } - PRINT(("kAudioDevicePropertyBufferSize = %d bytes\n", (int)uidata32 )); - - dataSize = sizeof( audioRange ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyBufferSizeRange, &dataSize, &audioRange ); - if( err != noErr ) - { - PRINT_ERR("Error reading buffer size range", err); - return; - } - PRINT(("kAudioDevicePropertyBufferSizeRange = %g to %g bytes\n", audioRange.mMinimum, audioRange.mMaximum )); - - dataSize = sizeof( uidata32 ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyBufferFrameSize, &dataSize, &uidata32 ); - if( err != noErr ) - { - PRINT_ERR("Error reading buffer size", err); - return; - } - PRINT(("kAudioDevicePropertyBufferFrameSize = %d frames\n", (int)uidata32 )); - - dataSize = sizeof( audioRange ); - err = AudioDeviceGetProperty( devID, 0, isInput, - kAudioDevicePropertyBufferFrameSizeRange, &dataSize, &audioRange ); - if( err != noErr ) - { - PRINT_ERR("Error reading buffer size range", err); - return; - } - PRINT(("kAudioDevicePropertyBufferFrameSizeRange = %g to %g frames\n", audioRange.mMinimum, audioRange.mMaximum )); - - return; -} -#endif - /*******************************************************************/ -static PaError PaOSX_OpenInputDevice( internalPortAudioStream *past ) +PaError PaHost_OpenInputStream( internalPortAudioStream *past ) { PaHostSoundControl *pahsc; const PaHostDeviceInfo *hostDeviceInfo; PaError result = paNoError; + UInt32 bytesPerHostBuffer; UInt32 dataSize; OSStatus err = noErr; - int needConverter = 0; - double deviceRate = past->past_SampleRate; - - DBUG(("PaOSX_OpenInputDevice: -------------\n")); + int bytesPerInputFrame; pahsc = (PaHostSoundControl *) past->past_DeviceData; + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_InputDeviceID)); if( (past->past_InputDeviceID < LOWEST_INPUT_DEVID) || (past->past_InputDeviceID > HIGHEST_INPUT_DEVID) ) { @@ -1029,134 +796,68 @@ static PaError PaOSX_OpenInputDevice( internalPortAudioStream *past ) } hostDeviceInfo = &sDeviceInfos[past->past_InputDeviceID]; - PaOSX_FixVolumeScalars( pahsc->input.audioDeviceID, IS_INPUT, - hostDeviceInfo->paInfo.maxInputChannels, 0.1, 0.9 ); - /* Try to set sample rate. */ - result = PaOSX_SetSampleRate( pahsc->input.audioDeviceID, IS_INPUT, past->past_SampleRate ); - if( result != paNoError ) - { - DBUG(("PaOSX_OpenInputDevice: Need converter for sample rate = %f\n", past->past_SampleRate )); - needConverter = 1; - result = paNoError; - } - else - { - DBUG(("PaOSX_OpenInputDevice: successfully set sample rate to %f\n", past->past_SampleRate )); - } + result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_INPUT, past->past_SampleRate ); + if( result != paNoError ) return result; - /* Try to set number of channels. */ - if( past->past_NumInputChannels > hostDeviceInfo->paInfo.maxInputChannels ) - { - return paInvalidChannelCount; /* Too many channels! */ - } - else if( past->past_NumInputChannels < hostDeviceInfo->paInfo.maxInputChannels ) + if( past->past_NumInputChannels != hostDeviceInfo->paInfo.maxInputChannels ) { - +#if 1 + return paInvalidChannelCount; // FIXME - how support mono? +#else +FIXME - should this be set on a stream basis? Is it possible to change? + /* Attempt to set number of channels. */ AudioStreamBasicDescription formatDesc; OSStatus err; memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mChannelsPerFrame = past->past_NumInputChannels; - err = AudioDeviceSetProperty( pahsc->input.audioDeviceID, 0, 0, + + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); - if (err != noErr) + if (err != kAudioHardwareNoError) { - needConverter = 1; + result = paInvalidChannelCount; + goto error; } +#endif } - /* Try to set the I/O bufferSize of the device. */ - dataSize = sizeof(pahsc->framesPerHostBuffer); - err = AudioDeviceSetProperty( pahsc->input.audioDeviceID, 0, 0, IS_INPUT, - kAudioDevicePropertyBufferFrameSize, dataSize, - &pahsc->framesPerHostBuffer); + // calculate native buffer sizes in bytes + bytesPerInputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels; + pahsc->pahsc_BytesPerUserNativeInputBuffer = past->past_FramesPerUserBuffer * bytesPerInputFrame; + bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerInputFrame; + + // Change the bufferSize of the device! Is this per device or just for our stream? + dataSize = sizeof(UInt32); + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_INPUT, + kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer); if( err != noErr ) { - DBUG(("PaOSX_OpenInputDevice: Need converter for buffer size = %d\n", pahsc->framesPerHostBuffer)); - needConverter = 1; + ERR_RPT(("Could not force buffer size!")); + result = paHostError; + goto error; } - - // setup PA conversion procedure + + // setup conversion procedure result = PaConvert_SetupInput( past, paFloat32 ); - - if( needConverter ) - { - AudioStreamBasicDescription sourceStreamFormat, destStreamFormat; - - /* Get source device format */ - dataSize = sizeof(sourceStreamFormat); - err = AudioDeviceGetProperty(pahsc->input.audioDeviceID, 0, IS_INPUT, - kAudioDevicePropertyStreamFormat, &dataSize, &sourceStreamFormat); - if( err != noErr ) - { - PRINT_ERR("PaOSX_OpenInputDevice: Could not get input device format", err); - sSavedHostError = err; - return paHostError; - } - deviceRate = sourceStreamFormat.mSampleRate; - DBUG(("PaOSX_OpenInputDevice: current device sample rate = %f\n", deviceRate )); - - /* Set target user format. */ - destStreamFormat = sourceStreamFormat; - destStreamFormat.mSampleRate = past->past_SampleRate; // sample rate of the user synthesis code - destStreamFormat.mChannelsPerFrame = past->past_NumInputChannels; // the number of channels in each frame - - err = AudioConverterNew ( - &sourceStreamFormat, - &destStreamFormat, - &pahsc->input.converter); - if( err != noErr ) - { - PRINT_ERR("Could not create input format converter", err); - sSavedHostError = err; - return paHostError; - } - } - - /* Allocate FIFO between Device callback and Converter callback so that device can push data - * and converter can pull data. - */ - if( needConverter || pahsc->usingSecondDevice ) - { - double sampleRateRatio; - long minSize, numBytes; - - /* Allocate an input buffer because we need it between the user callback and the converter. */ - pahsc->input.converterBuffer = PaHost_AllocateFastMemory( pahsc->input.bytesPerUserNativeBuffer ); - if( pahsc->input.converterBuffer == NULL ) - { - return paInsufficientMemory; - } - sampleRateRatio = deviceRate / past->past_SampleRate; - minSize = pahsc->input.bytesPerUserNativeBuffer * 4 * sampleRateRatio; - numBytes = RoundUpToNextPowerOf2( minSize ); - DBUG(("PaOSX_OpenInputDevice: FIFO numBytes = %ld\n", numBytes)); - pahsc->ringBufferData = PaHost_AllocateFastMemory( numBytes ); - if( pahsc->ringBufferData == NULL ) - { - return paInsufficientMemory; - } - RingBuffer_Init( &pahsc->ringBuffer, numBytes, pahsc->ringBufferData ); - // make it look full at beginning - RingBuffer_AdvanceWriteIndex( &pahsc->ringBuffer, numBytes ); - } - + return result; + +error: return result; } /*******************************************************************/ -static PaError PaOSX_OpenOutputDevice( internalPortAudioStream *past ) +PaError PaHost_OpenOutputStream( internalPortAudioStream *past ) { PaHostSoundControl *pahsc; const PaHostDeviceInfo *hostDeviceInfo; PaError result = paNoError; + UInt32 bytesPerHostBuffer; UInt32 dataSize; OSStatus err = noErr; - int needConverter = 0; + int bytesPerOutputFrame; - DBUG(("PaOSX_OpenOutputDevice: -------------\n")); - pahsc = (PaHostSoundControl *) past->past_DeviceData; DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID)); @@ -1165,141 +866,101 @@ static PaError PaOSX_OpenOutputDevice( internalPortAudioStream *past ) { return paInvalidDeviceId; } - hostDeviceInfo = &sDeviceInfos[past->past_OutputDeviceID]; - - //PaOSX_DumpDeviceInfo( pahsc->output.audioDeviceID, IS_OUTPUT ); - PaOSX_FixVolumeScalars( pahsc->output.audioDeviceID, IS_OUTPUT, - hostDeviceInfo->paInfo.maxOutputChannels, 0.1, 0.9 ); - /* Try to set sample rate. */ - result = PaOSX_SetSampleRate( pahsc->output.audioDeviceID, IS_OUTPUT, past->past_SampleRate ); - if( result != paNoError ) - { - DBUG(("PaOSX_OpenOutputDevice: Need converter for sample rate = %f\n", past->past_SampleRate )); - needConverter = 1; - result = paNoError; - } - else - { - DBUG(("PaOSX_OpenOutputDevice: successfully set sample rate to %f\n", past->past_SampleRate )); - } + result = PaHost_SetSampleRate( hostDeviceInfo->audioDeviceID, IS_OUTPUT, past->past_SampleRate ); + if( result != paNoError ) return result; - if( past->past_NumOutputChannels > hostDeviceInfo->paInfo.maxOutputChannels ) - { - return paInvalidChannelCount; /* Too many channels! */ - } - else + if( past->past_NumOutputChannels != hostDeviceInfo->paInfo.maxOutputChannels ) { +#if 1 + return paInvalidChannelCount; // FIXME - how support mono? +#else /* Attempt to set number of channels. */ AudioStreamBasicDescription formatDesc; OSStatus err; memset( &formatDesc, 0, sizeof(AudioStreamBasicDescription) ); formatDesc.mChannelsPerFrame = past->past_NumOutputChannels; - err = AudioDeviceSetProperty( pahsc->output.audioDeviceID, 0, 0, + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT, kAudioDevicePropertyStreamFormat, sizeof(formatDesc), &formatDesc); + if (err != kAudioHardwareNoError) { - DBUG(("PaOSX_OpenOutputDevice: Need converter for num channels.\n")); - needConverter = 1; + result = paInvalidChannelCount; + goto error; } +#endif } - /* Change the I/O bufferSize of the device. */ - dataSize = sizeof(pahsc->framesPerHostBuffer); - err = AudioDeviceSetProperty( pahsc->output.audioDeviceID, 0, 0, IS_OUTPUT, - kAudioDevicePropertyBufferFrameSize, dataSize, - &pahsc->framesPerHostBuffer); + // calculate buffer sizes in bytes + bytesPerOutputFrame = Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels; + pahsc->pahsc_BytesPerUserNativeOutputBuffer = past->past_FramesPerUserBuffer * bytesPerOutputFrame; + bytesPerHostBuffer = pahsc->pahsc_FramesPerHostBuffer * bytesPerOutputFrame; + + // Change the bufferSize of the device! Is this per device or just for our stream? + dataSize = sizeof(bytesPerHostBuffer); + err = AudioDeviceSetProperty( hostDeviceInfo->audioDeviceID, 0, 0, IS_OUTPUT, + kAudioDevicePropertyBufferSize, dataSize, &bytesPerHostBuffer); if( err != noErr ) { - DBUG(("PaOSX_OpenOutputDevice: Need converter for buffer size = %d\n", pahsc->framesPerHostBuffer)); - needConverter = 1; + ERR_RPT(("Could not force buffer size!")); + result = paHostError; + goto error; } - + // setup conversion procedure result = PaConvert_SetupOutput( past, paFloat32 ); - if( needConverter ) - { - AudioStreamBasicDescription sourceStreamFormat, destStreamFormat; - DBUG(("PaOSX_OpenOutputDevice: using AUConverter!\n")); - /* Get target device format */ - dataSize = sizeof(destStreamFormat); - err = AudioDeviceGetProperty(pahsc->output.audioDeviceID, 0, IS_OUTPUT, - kAudioDevicePropertyStreamFormat, &dataSize, &destStreamFormat); - if( err != noErr ) - { - PRINT_ERR("PaOSX_OpenOutputDevice: Could not get output device format", err); - sSavedHostError = err; - return paHostError; - } + return result; - /* Set source user format. */ - sourceStreamFormat = destStreamFormat; - sourceStreamFormat.mSampleRate = past->past_SampleRate; // sample rate of the user synthesis code - sourceStreamFormat.mChannelsPerFrame = past->past_NumOutputChannels; // the number of channels in each frame - - /* Allocate an output buffer because we need it between the user callback and the converter. */ - pahsc->output.converterBuffer = PaHost_AllocateFastMemory( pahsc->output.bytesPerUserNativeBuffer ); - err = AudioConverterNew ( - &sourceStreamFormat, - &destStreamFormat, - &pahsc->output.converter); - if( err != noErr ) - { - PRINT_ERR("Could not create output format converter", err); - sSavedHostError = err; - return paHostError; - } - } - +error: return result; } +/*******************************************************************/ +PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + return pahsc->pahsc_FramesPerHostBuffer; +} + + /******************************************************************* * Determine how many User Buffers we can put into our CoreAudio stream buffer. * Uses: * past->past_FramesPerUserBuffer, etc. * Sets: * past->past_NumUserBuffers -* pahsc->framesPerHostBuffer -* pahsc->input.bytesPerUserNativeBuffer -* pahsc->output.bytesPerUserNativeBuffer +* pahsc->pahsc_UserBuffersPerHostBuffer +* pahsc->pahsc_FramesPerHostBuffer */ -static void PaOSX_CalcHostBufferSize( internalPortAudioStream *past ) +static void PaHost_CalcHostBufferSize( internalPortAudioStream *past ) { PaHostSoundControl *pahsc = ( PaHostSoundControl *)past->past_DeviceData; + unsigned int minNumUserBuffers; - // Determine number of user buffers based strictly on minimum reasonable buffer size. - // We ignore the Pa_OpenStream numBuffer parameter because CoreAudio has a big - // mix buffer and handles latency automatically. - past->past_NumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + // Determine number of user buffers based on minimum latency. + minNumUserBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + // Compare to user requested number in user wants more than the minimum. + past->past_NumUserBuffers = ( minNumUserBuffers > past->past_NumUserBuffers ) ? + minNumUserBuffers : past->past_NumUserBuffers; + DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); + // For CoreAudio, we only have one Host buffer, so... + pahsc->pahsc_UserBuffersPerHostBuffer = past->past_NumUserBuffers; // Calculate size of CoreAudio buffer. - pahsc->framesPerHostBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; + pahsc->pahsc_FramesPerHostBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; - // calculate buffer sizes in bytes - pahsc->input.bytesPerUserNativeBuffer = past->past_FramesPerUserBuffer * - Pa_GetSampleSize(paFloat32) * past->past_NumInputChannels; - pahsc->output.bytesPerUserNativeBuffer = past->past_FramesPerUserBuffer * - Pa_GetSampleSize(paFloat32) * past->past_NumOutputChannels; - - DBUG(("PaOSX_CalcNumHostBuffers: past_NumUserBuffers = %ld\n", past->past_NumUserBuffers )); - DBUG(("PaOSX_CalcNumHostBuffers: framesPerHostBuffer = %d\n", pahsc->framesPerHostBuffer )); - DBUG(("PaOSX_CalcNumHostBuffers: input.bytesPerUserNativeBuffer = %d\n", pahsc->input.bytesPerUserNativeBuffer )); - DBUG(("PaOSX_CalcNumHostBuffers: output.bytesPerUserNativeBuffer = %d\n", pahsc->output.bytesPerUserNativeBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer )); } -/*****************************************************************************/ -/************** Internal Host API ********************************************/ -/*****************************************************************************/ +/*******************************************************************/ PaError PaHost_OpenStream( internalPortAudioStream *past ) { PaError result = paNoError; PaHostSoundControl *pahsc; - Boolean useInput; - Boolean useOutput; /* Allocate and initialize host data. */ pahsc = (PaHostSoundControl *) malloc(sizeof(PaHostSoundControl)); @@ -1310,53 +971,43 @@ PaError PaHost_OpenStream( internalPortAudioStream *past ) } memset( pahsc, 0, sizeof(PaHostSoundControl) ); past->past_DeviceData = (void *) pahsc; - pahsc->primaryDeviceID = kAudioDeviceUnknown; - pahsc->input.audioDeviceID = kAudioDeviceUnknown; - pahsc->output.audioDeviceID = kAudioDeviceUnknown; - - PaOSX_CalcHostBufferSize( past ); - - /* Setup constants for CPU load measurement. */ - pahsc->inverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->framesPerHostBuffer); - useOutput = (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0); - useInput = (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0); - - // Set device IDs - if( useOutput ) + // If we are using both input and out, then they must be on the same CoreAudio device, + // until we implement a FIFO between two devices. + if( (past->past_OutputDeviceID != paNoDevice) && (past->past_InputDeviceID != paNoDevice) ) { - pahsc->output.audioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; - pahsc->primaryDeviceID = pahsc->output.audioDeviceID; - if( useInput ) + AudioDeviceID inputID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + AudioDeviceID outputID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; + if( inputID != outputID ) { - pahsc->input.audioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; - if( pahsc->input.audioDeviceID != pahsc->primaryDeviceID ) - { - pahsc->usingSecondDevice = TRUE; // Use two separate devices! - } + ERR_RPT(("PortAudio: input and output must use same CoreAudio device!\n")); + return paInvalidDeviceId; } } - else + + PaHost_CalcHostBufferSize( past ); + { - /* Just input, not output. */ - pahsc->input.audioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; - pahsc->primaryDeviceID = pahsc->input.audioDeviceID; + int msecLatency = (int) ((PaHost_GetTotalBufferFrames(past) * 1000) / past->past_SampleRate); + PRINT(("PortAudio on OS X - Latency = %d frames, %d msec\n", PaHost_GetTotalBufferFrames(past), msecLatency )); } - DBUG(("outputDeviceID = %ld\n", pahsc->output.audioDeviceID )); - DBUG(("inputDeviceID = %ld\n", pahsc->input.audioDeviceID )); - DBUG(("primaryDeviceID = %ld\n", pahsc->primaryDeviceID )); + /* Setup constants for CPU load measurement. */ + pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer); + /* ------------------ OUTPUT */ - if( useOutput ) + if( (past->past_OutputDeviceID != paNoDevice) && (past->past_NumOutputChannels > 0) ) { - result = PaOSX_OpenOutputDevice( past ); + pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_OutputDeviceID].audioDeviceID; + result = PaHost_OpenOutputStream( past ); if( result < 0 ) goto error; } /* ------------------ INPUT */ - if( useInput ) + if( (past->past_InputDeviceID != paNoDevice) && (past->past_NumInputChannels > 0) ) { - result = PaOSX_OpenInputDevice( past ); + pahsc->pahsc_AudioDeviceID = sDeviceInfos[past->past_InputDeviceID].audioDeviceID; + result = PaHost_OpenInputStream( past ); if( result < 0 ) goto error; } @@ -1390,49 +1041,21 @@ PaError PaHost_StartEngine( internalPortAudioStream *past ) past->past_StopNow = 0; past->past_IsActive = 1; -/* If full duplex and using two separate devices then start input device. */ - if( pahsc->usingSecondDevice ) - { - // Associate an IO proc with the device and pass a pointer to the audio data context - err = AudioDeviceAddIOProc(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback, past); - if (err != noErr) - { - PRINT_ERR("PaHost_StartEngine: AudioDeviceAddIOProc secondary failed", err ); - goto error; - } - - // start playing sound through the device - err = AudioDeviceStart(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); - if (err != noErr) - { - PRINT_ERR("PaHost_StartEngine: AudioDeviceStart secondary failed", err ); - PRINT(("The program may succeed if you run it again!\n")); - goto error; - } - } - // Associate an IO proc with the device and pass a pointer to the audio data context - err = AudioDeviceAddIOProc(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback, past); - if (err != noErr) - { - PRINT_ERR("PaHost_StartEngine: AudioDeviceAddIOProc primary failed", err ); - goto error; - } + err = AudioDeviceAddIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc, past); + if (err != noErr) goto error; // start playing sound through the device - err = AudioDeviceStart(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); - if (err != noErr) - { - PRINT_ERR("PaHost_StartEngine: AudioDeviceStart primary failed", err ); - PRINT(("The program may succeed if you run it again!\n")); - goto error; - } - + err = AudioDeviceStart(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto error; return result; +#if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_StartEngine: TimeSlice() returned ", result ); +#endif + error: - sSavedHostError = err; - return paHostError; + return paHostError; // FIXME - save host error } /*************************************************************************/ @@ -1454,29 +1077,16 @@ PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) #endif // FIXME - we should ask proc to stop instead of stopping abruptly - err = AudioDeviceStop(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); - if (err != noErr) - { - goto error; - } + err = AudioDeviceStop(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto Bail; - err = AudioDeviceRemoveIOProc(pahsc->primaryDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioIOCallback); - if (err != noErr) goto error; - -/* If full duplex and using two separate devices then start input device. */ - if( pahsc->usingSecondDevice ) - { - err = AudioDeviceStop(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); - if (err != noErr) goto error; - err = AudioDeviceRemoveIOProc(pahsc->input.audioDeviceID, (AudioDeviceIOProc)PaOSX_CoreAudioInputCallback); - if (err != noErr) goto error; - } + err = AudioDeviceRemoveIOProc(pahsc->pahsc_AudioDeviceID, (AudioDeviceIOProc)appIOProc); + if (err != noErr) goto Bail; return paNoError; -error: - sSavedHostError = err; - return paHostError; +Bail: + return paHostError; // FIXME - save err } /*************************************************************************/ @@ -1499,47 +1109,70 @@ PaError PaHost_CloseStream( internalPortAudioStream *past ) if( past == NULL ) return paBadStreamPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; if( pahsc == NULL ) return paNoError; - - //PaOSX_DumpDeviceInfo( sDeviceInfos[past->past_OutputDeviceID].audioDeviceID, IS_OUTPUT ); #if PA_TRACE_START_STOP AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); #endif - if( pahsc->output.converterBuffer != NULL ) - { - PaHost_FreeFastMemory( pahsc->output.converterBuffer, pahsc->output.bytesPerUserNativeBuffer ); - } - if( pahsc->input.converterBuffer != NULL ) - { - PaHost_FreeFastMemory( pahsc->input.converterBuffer, pahsc->input.bytesPerUserNativeBuffer ); - } - if( pahsc->ringBufferData != NULL ) - { - PaHost_FreeFastMemory( pahsc->ringBufferData, pahsc->ringBuffer.bufferSize ); - } - if( pahsc->output.converter != NULL ) - { - verify_noerr(AudioConverterDispose (pahsc->output.converter)); - } - if( pahsc->input.converter != NULL ) - { - verify_noerr(AudioConverterDispose (pahsc->input.converter)); - } - free( pahsc ); past->past_DeviceData = NULL; return paNoError; } -/********************************************************************** -** Initialize Host dependant part of API. +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** +** set PA_MIN_LATENCY_MSEC=200 +** +** in the cshrc file. */ -PaError PaHost_Init( void ) +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") + +#if 1 +int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) { - return PaOSX_MaybeQueryDevices(); + int minBuffers; + int denominator; + int minLatencyMsec = PA_MIN_LATENCY_MSEC; + char *minLatencyText = getenv(PA_LATENCY_ENV_NAME); + if( minLatencyText != NULL ) + { + PRINT(("PA_MIN_LATENCY_MSEC = %s\n", minLatencyText )); + minLatencyMsec = atoi( minLatencyText ); + if( minLatencyMsec < 1 ) minLatencyMsec = 1; + else if( minLatencyMsec > 5000 ) minLatencyMsec = 5000; + } + + denominator = 1000.0 * framesPerBuffer; + minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator ); + if( minBuffers < 1 ) minBuffers = 1; + return minBuffers; } +#else +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. +*/ +int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) +{ + int minUserBuffers; + int minFramesPerHostBuffer; + + // Calculate minimum and maximum sizes based on timing and sample rate. + minFramesPerHostBuffer = (int) (PA_MIN_LATENCY_MSEC * sampleRate / 1000.0); + // round up to nearest multiple of 8 + minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7; + DBUG(("Pa_GetMinNumBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer )); + + minUserBuffers = (minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer; + if( minUserBuffers < 1 ) minUserBuffers = 1; + + return minUserBuffers; +} +#endif /************************************************************************* ** Cleanup device info. @@ -1552,10 +1185,8 @@ PaError PaHost_Term( void ) { for( i=0; ipast_IsActive; } -/*******************************************************************/ -PaError PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) -{ - PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; - return pahsc->framesPerHostBuffer; -} - -/*****************************************************************************/ -/************** External User API ********************************************/ -/*****************************************************************************/ - -/********************************************************************** -** Query devices and use result. -*/ -PaDeviceID Pa_GetDefaultInputDeviceID( void ) -{ - PaError result = PaOSX_MaybeQueryDevices(); - if( result < 0 ) return result; - return sDefaultInputDeviceID; -} - -PaDeviceID Pa_GetDefaultOutputDeviceID( void ) -{ - PaError result = PaOSX_MaybeQueryDevices(); - if( result < 0 ) return result; - return sDefaultOutputDeviceID; -} - - -/************************************************************************* -** Determine minimum number of buffers required for this host based -** on minimum latency. Because CoreAudio manages latency, this just sets -** a reasonable small buffer size. -*/ -int Pa_GetMinNumBuffers( int framesPerBuffer, double framesPerSecond ) -{ - int minBuffers; - double denominator; - int minLatencyMsec = PA_MIN_LATENCY_MSEC; - denominator = 1000.0 * framesPerBuffer; - minBuffers = (int) (((minLatencyMsec * framesPerSecond) + denominator - 1) / denominator ); - if( minBuffers < 1 ) minBuffers = 1; - return minBuffers; -} - -/*************************************************************************/ -void Pa_Sleep( long msec ) -{ - usleep( msec * 1000 ); -} - /*************************************************************************/ PaTimestamp Pa_StreamTime( PortAudioStream *stream ) { @@ -1659,37 +1252,10 @@ PaTimestamp Pa_StreamTime( PortAudioStream *stream ) if( past == NULL ) return paBadStreamPtr; pahsc = (PaHostSoundControl *) past->past_DeviceData; - AudioDeviceGetCurrentTime(pahsc->primaryDeviceID, &timeStamp); + AudioDeviceGetCurrentTime(pahsc->pahsc_AudioDeviceID, &timeStamp); streamTime = ( timeStamp.mFlags & kAudioTimeStampSampleTimeValid) ? timeStamp.mSampleTime : past->past_FrameCount; return streamTime; } - -/************************************************************************************/ -long Pa_GetHostError() -{ - return sSavedHostError; -} - -/*************************************************************************/ -int Pa_CountDevices() -{ - if( sNumPaDevices <= 0 ) Pa_Initialize(); - return sNumPaDevices; -} - -/************************************************************************* -** PaDeviceInfo structures have already been created -** so just return the pointer. -** -*/ -const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) -{ - if( id < 0 || id >= sNumPaDevices ) - return NULL; - - return &sDeviceInfos[id].paInfo; -} - diff --git a/pd/portaudio/pa_mac_sm/pa_mac_sm.c b/pd/portaudio/pa_mac_sm/pa_mac_sm.c new file mode 100644 index 00000000..59457ded --- /dev/null +++ b/pd/portaudio/pa_mac_sm/pa_mac_sm.c @@ -0,0 +1,1656 @@ +/* + * $Id: pa_mac_sm.c,v 1.1.2.1 2002/06/07 21:20:48 rossb Exp $ + * Portable Audio I/O Library for Macintosh + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2000 Phil Burk + * + * Special thanks to Chris Rolfe for his many helpful suggestions, bug fixes, + * and code contributions. + * Thanks also to Tue Haste Andersen, Alberto Ricci, Nico Wald, + * Roelf Toxopeus and Tom Erbe for testing the code and making + * numerous suggestions. + * + * 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 + PLB20010415 - ScanInputDevices was setting sDefaultOutputDeviceID instead of sDefaultInputDeviceID + PLB20010415 - Device Scan was crashing for anything other than siBadSoundInDevice, but some Macs may return other errors! + PLB20010420 - Fix TIMEOUT in record mode. + PLB20010420 - Change CARBON_COMPATIBLE to TARGET_API_MAC_CARBON + PLB20010907 - Pass unused event to WaitNextEvent to prevent Mac OSX crash. Thanks Dominic Mazzoni. + PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni. + PLB20011009 - Use NewSndCallBackUPP() for CARBON + PLB20020417 - I used to call Pa_GetMinNumBuffers() which doesn't take into account the + variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will + give lower latency when virtual memory is turned off. + Thanks Kristoffer Jensen and Georgios Marentakis for spotting this bug. + PLB20020423 - Use new method to calculate CPU load similar to other ports. Based on num frames calculated. + Fixed Pa_StreamTime(). Now estimates how many frames have played based on MicroSecond timer. + Added PA_MAX_USAGE_ALLOWED to prevent Mac form hanging when CPU load approaches 100%. + PLB20020424 - Fixed return value in Pa_StreamTime +*/ + +/* +COMPATIBILITY +This Macintosh implementation is designed for use with Mac OS 7, 8 and +9 on PowerMacs, and OS X if compiled with CARBON + +OUTPUT +A circular array of CmpSoundHeaders is used as a queue. For low latency situations +there will only be two small buffers used. For higher latency, more and larger buffers +may be used. +To play the sound we use SndDoCommand() with bufferCmd. Each buffer is followed +by a callbackCmd which informs us when the buffer has been processsed. + +INPUT +The SndInput Manager SPBRecord call is used for sound input. If only +input is used, then the PA user callback is called from the Input completion proc. +For full-duplex, or output only operation, the PA callback is called from the +HostBuffer output completion proc. In that case, input sound is passed to the +callback by a simple FIFO. + +TODO: +O- Add support for native sample data formats other than int16. +O- Review buffer sizing. Should it be based on result of siDeviceBufferInfo query? +O- Determine default devices somehow. +*/ +#include +#include +#include +#include +#include + +/* Mac specific includes */ +#include "OSUtils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +#ifndef FALSE + #define FALSE (0) + #define TRUE (!FALSE) +#endif + +/* #define TARGET_API_MAC_CARBON (1) */ + +/* + * Define maximum CPU load that will be allowed. User callback will + * be skipped if load exceeds this limit. This is to prevent the Mac + * from hanging when the CPU is hogged by the sound thread. + * On my PowerBook G3, the mac hung when I used 94% of CPU ( usage = 0.94 ). + */ +#define PA_MAX_USAGE_ALLOWED (0.92) + +/* Debugging output macros. */ +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) /**/ +#define DBUGX(x) /* PRINT(x) /**/ + +#define MAC_PHYSICAL_FRAMES_PER_BUFFER (512) /* Minimum number of stereo frames per SoundManager double buffer. */ +#define MAC_VIRTUAL_FRAMES_PER_BUFFER (4096) /* Need this many when using Virtual Memory for recording. */ +#define PA_MIN_NUM_HOST_BUFFERS (2) +#define PA_MAX_NUM_HOST_BUFFERS (16) /* Do not exceed!! */ +#define PA_MAX_DEVICE_INFO (32) + +/* Conversions for 16.16 fixed point code. */ +#define DoubleToUnsignedFixed(x) ((UnsignedFixed) ((x) * 65536.0)) +#define UnsignedFixedToDouble(fx) (((double)(fx)) * (1.0/(1<<16))) + +/************************************************************************************/ +/****************** Structures ******************************************************/ +/************************************************************************************/ +/* Use for passing buffers from input callback to output callback for processing. */ +typedef struct MultiBuffer +{ + char *buffers[PA_MAX_NUM_HOST_BUFFERS]; + int numBuffers; + int nextWrite; + int nextRead; +} +MultiBuffer; + +/* Define structure to contain all Macintosh specific data. */ +typedef struct PaHostSoundControl +{ + UInt64 pahsc_EntryCount; + double pahsc_InverseMicrosPerHostBuffer; /* 1/Microseconds of real-time audio per user buffer. */ + + /* Use char instead of Boolean for atomic operation. */ + volatile char pahsc_IsRecording; /* Recording in progress. Set by foreground. Cleared by background. */ + volatile char pahsc_StopRecording; /* Signal sent to background. */ + volatile char pahsc_IfInsideCallback; + /* Input */ + SPB pahsc_InputParams; + SICompletionUPP pahsc_InputCompletionProc; + MultiBuffer pahsc_InputMultiBuffer; + int32 pahsc_BytesPerInputHostBuffer; + int32 pahsc_InputRefNum; + /* Output */ + CmpSoundHeader pahsc_SoundHeaders[PA_MAX_NUM_HOST_BUFFERS]; + int32 pahsc_BytesPerOutputHostBuffer; + SndChannelPtr pahsc_Channel; + SndCallBackUPP pahsc_OutputCompletionProc; + int32 pahsc_NumOutsQueued; + int32 pahsc_NumOutsPlayed; + PaTimestamp pahsc_NumFramesDone; + UInt64 pahsc_WhenFramesDoneIncremented; + /* Init Time -------------- */ + int32 pahsc_NumHostBuffers; + int32 pahsc_FramesPerHostBuffer; + int32 pahsc_UserBuffersPerHostBuffer; + int32 pahsc_MinFramesPerHostBuffer; /* Can vary depending on virtual memory usage. */ +} +PaHostSoundControl; + +/* Mac specific device information. */ +typedef struct internalPortAudioDevice +{ + long pad_DeviceRefNum; + long pad_DeviceBufferSize; + Component pad_Identifier; + PaDeviceInfo pad_Info; +} +internalPortAudioDevice; + +/************************************************************************************/ +/****************** Data ************************************************************/ +/************************************************************************************/ +static int sNumDevices = 0; +static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 }; +static int32 sPaHostError = 0; +static int sDefaultOutputDeviceID; +static int sDefaultInputDeviceID; + +/************************************************************************************/ +/****************** Prototypes ******************************************************/ +/************************************************************************************/ +static PaError PaMac_TimeSlice( internalPortAudioStream *past, int16 *macOutputBufPtr ); +static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr ); +static PaError PaMac_RecordNext( internalPortAudioStream *past ); +static void PaMac_StartLoadCalculation( internalPortAudioStream *past ); +static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerBuffer, double sampleRate ); +static double *PaMac_GetSampleRatesFromHandle ( int numRates, Handle h ); +static PaError PaMac_ScanInputDevices( void ); +static PaError PaMac_ScanOutputDevices( void ); +static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad ); +static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad ); +static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader ); +static void PaMac_EndLoadCalculation( internalPortAudioStream *past ); +static void PaMac_PlayNext ( internalPortAudioStream *past, int index ); +static long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index ); +static pascal void PaMac_InputCompletionProc(SPBPtr recParams); +static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCmd); +static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index ); +long PaHost_GetTotalBufferFrames( internalPortAudioStream *past ); +static int Mac_IsVirtualMemoryOn( void ); +static void PToCString(unsigned char* inString, char* outString); +char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf ); +char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf ); +int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf ); +int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf ); +int MultiBuffer_IsWriteable( MultiBuffer *mbuf ); +int MultiBuffer_IsReadable( MultiBuffer *mbuf ); +void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf ); +void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf ); + +/************************************************************************* +** Simple FIFO index control for multiple buffers. +** Read and Write indices range from 0 to 2N-1. +** This allows us to distinguish between full and empty. +*/ +char *MultiBuffer_GetNextWriteBuffer( MultiBuffer *mbuf ) +{ + return mbuf->buffers[mbuf->nextWrite % mbuf->numBuffers]; +} +char *MultiBuffer_GetNextReadBuffer( MultiBuffer *mbuf ) +{ + return mbuf->buffers[mbuf->nextRead % mbuf->numBuffers]; +} +int MultiBuffer_GetNextReadIndex( MultiBuffer *mbuf ) +{ + return mbuf->nextRead % mbuf->numBuffers; +} +int MultiBuffer_GetNextWriteIndex( MultiBuffer *mbuf ) +{ + return mbuf->nextWrite % mbuf->numBuffers; +} + +int MultiBuffer_IsWriteable( MultiBuffer *mbuf ) +{ + int bufsFull = mbuf->nextWrite - mbuf->nextRead; + if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers); + return (bufsFull < mbuf->numBuffers); +} +int MultiBuffer_IsReadable( MultiBuffer *mbuf ) +{ + int bufsFull = mbuf->nextWrite - mbuf->nextRead; + if( bufsFull < 0 ) bufsFull += (2 * mbuf->numBuffers); + return (bufsFull > 0); +} +void MultiBuffer_AdvanceReadIndex( MultiBuffer *mbuf ) +{ + int temp = mbuf->nextRead + 1; + mbuf->nextRead = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp; +} +void MultiBuffer_AdvanceWriteIndex( MultiBuffer *mbuf ) +{ + int temp = mbuf->nextWrite + 1; + mbuf->nextWrite = (temp >= (2 * mbuf->numBuffers)) ? 0 : temp; +} + +/************************************************************************* +** String Utility by Chris Rolfe +*/ +static void PToCString(unsigned char* inString, char* outString) +{ + long i; + for(i=0; isampleRates; + if( (rates != NULL) ) free( rates ); /* MEM_011 */ + dev->sampleRates = NULL; + if( dev->name != NULL ) free( (void *) dev->name ); /* MEM_010 */ + dev->name = NULL; + } + sNumDevices = 0; + return paNoError; +} + +/************************************************************************* + PaHost_Init() is the library initialization function - call this before + using the library. +*/ +PaError PaHost_Init( void ) +{ + PaError err; + NumVersionVariant version; + + version.parts = SndSoundManagerVersion(); + DBUG(("SndSoundManagerVersion = 0x%x\n", version.whole)); + + /* Have we already initialized the device info? */ + err = (PaError) Pa_CountDevices(); + if( err < 0 ) return err; + else return paNoError; +} + +/************************************************************************* + PaMac_ScanOutputDevices() queries the properties of all output devices. +*/ +static PaError PaMac_ScanOutputDevices( void ) +{ + PaError err; + Component identifier=0; + ComponentDescription criteria = { kSoundOutputDeviceType, 0, 0, 0, 0 }; + long numComponents, i; + + /* Search the system linked list for output components */ + numComponents = CountComponents (&criteria); + identifier = 0; + sDefaultOutputDeviceID = sNumDevices; /* FIXME - query somehow */ + for (i = 0; i < numComponents; i++) + { + /* passing nil returns first matching component. */ + identifier = FindNextComponent( identifier, &criteria); + sDevices[sNumDevices].pad_Identifier = identifier; + + /* Set up for default OUTPUT devices. */ + err = PaMac_QueryOutputDeviceInfo( identifier, &sDevices[sNumDevices] ); + if( err < 0 ) return err; + else sNumDevices++; + + } + + return paNoError; +} + +/************************************************************************* + PaMac_ScanInputDevices() queries the properties of all input devices. +*/ +static PaError PaMac_ScanInputDevices( void ) +{ + Str255 deviceName; + int count; + Handle iconHandle; + PaError err; + OSErr oserr; + count = 1; + sDefaultInputDeviceID = sNumDevices; /* FIXME - query somehow */ /* PLB20010415 - was setting sDefaultOutputDeviceID */ + while(true) + { + /* Thanks Chris Rolfe and Alberto Ricci for this trick. */ + oserr = SPBGetIndexedDevice(count++, deviceName, &iconHandle); + DBUG(("PaMac_ScanInputDevices: SPBGetIndexedDevice returned %d\n", oserr )); +#if 1 + /* PLB20010415 - was giving error for anything other than siBadSoundInDevice, but some Macs may return other errors! */ + if(oserr != noErr) break; /* Some type of error is expected when count > devices */ +#else + if(oserr == siBadSoundInDevice) + { /* it's expected when count > devices */ + oserr = noErr; + break; + } + if(oserr != noErr) + { + ERR_RPT(("ERROR: SPBGetIndexedDevice(%d,,) returned %d\n", count-1, oserr )); + sPaHostError = oserr; + return paHostError; + } +#endif + DisposeHandle(iconHandle); /* Don't need the icon */ + + err = PaMac_QueryInputDeviceInfo( deviceName, &sDevices[sNumDevices] ); + DBUG(("PaMac_ScanInputDevices: PaMac_QueryInputDeviceInfo returned %d\n", err )); + if( err < 0 ) return err; + else if( err == 1 ) sNumDevices++; + } + + return paNoError; +} + +/* Sample rate info returned by using siSampleRateAvailable selector in SPBGetDeviceInfo() */ +/* Thanks to Chris Rolfe for help with this query. */ +#pragma options align=mac68k +typedef struct +{ + int16 numRates; + UnsignedFixed (**rates)[]; /* Handle created by SPBGetDeviceInfo */ +} +SRateInfo; +#pragma options align=reset + +/************************************************************************* +** PaMac_QueryOutputDeviceInfo() +** Query information about a named output device. +** Clears contents of ipad and writes info based on queries. +** Return one if OK, +** zero if device cannot be used, +** or negative error. +*/ +static PaError PaMac_QueryOutputDeviceInfo( Component identifier, internalPortAudioDevice *ipad ) +{ + int len; + OSErr err; + PaDeviceInfo *dev = &ipad->pad_Info; + SRateInfo srinfo = {0}; + int numRates; + ComponentDescription tempD; + Handle nameH=nil, infoH=nil, iconH=nil; + + memset( ipad, 0, sizeof(internalPortAudioDevice) ); + + dev->structVersion = 1; + dev->maxInputChannels = 0; + dev->maxOutputChannels = 2; + dev->nativeSampleFormats = paInt16; /* FIXME - query to see if 24 or 32 bit data can be handled. */ + + /* Get sample rates supported. */ + err = GetSoundOutputInfo(identifier, siSampleRateAvailable, (Ptr) &srinfo); + if(err != noErr) + { + ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetSoundOutputInfo siSampleRateAvailable returned %d\n", err )); + goto error; + } + numRates = srinfo.numRates; + DBUG(("PaMac_QueryOutputDeviceInfo: srinfo.numRates = 0x%x\n", srinfo.numRates )); + if( numRates == 0 ) + { + dev->numSampleRates = -1; + numRates = 2; + } + else + { + dev->numSampleRates = numRates; + } + dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates ); + /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */ + DisposeHandle((Handle) srinfo.rates); + + /* Device name */ + /* we pass an existing handle for the component name; + we don't care about the info (type, subtype, etc.) or icon, so set them to nil */ + infoH = nil; + iconH = nil; + nameH = NewHandle(0); + if(nameH == nil) return paInsufficientMemory; + err = GetComponentInfo(identifier, &tempD, nameH, infoH, iconH); + if (err) + { + ERR_RPT(("Error in PaMac_QueryOutputDeviceInfo: GetComponentInfo returned %d\n", err )); + goto error; + } + len = (*nameH)[0] + 1; + dev->name = (char *) malloc(len); /* MEM_010 */ + if( dev->name == NULL ) + { + DisposeHandle(nameH); + return paInsufficientMemory; + } + else + { + PToCString((unsigned char *)(*nameH), (char *) dev->name); + DisposeHandle(nameH); + } + + DBUG(("PaMac_QueryOutputDeviceInfo: dev->name = %s\n", dev->name )); + return paNoError; + +error: + sPaHostError = err; + return paHostError; + +} + +/************************************************************************* +** PaMac_QueryInputDeviceInfo() +** Query information about a named input device. +** Clears contents of ipad and writes info based on queries. +** Return one if OK, +** zero if device cannot be used, +** or negative error. +*/ +static PaError PaMac_QueryInputDeviceInfo( Str255 deviceName, internalPortAudioDevice *ipad ) +{ + PaError result = paNoError; + int len; + OSErr err; + long mRefNum = 0; + long tempL; + int16 tempS; + Fixed tempF; + PaDeviceInfo *dev = &ipad->pad_Info; + SRateInfo srinfo = {0}; + int numRates; + + memset( ipad, 0, sizeof(internalPortAudioDevice) ); + dev->maxOutputChannels = 0; + + /* Open device based on name. If device is in use, it may not be able to open in write mode. */ + err = SPBOpenDevice( deviceName, siWritePermission, &mRefNum); + if (err) + { + /* If device is in use, it may not be able to open in write mode so try read mode. */ + err = SPBOpenDevice( deviceName, siReadPermission, &mRefNum); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBOpenDevice returned %d\n", err )); + sPaHostError = err; + return paHostError; + } + } + + /* Define macros for printing out device info. */ +#define PrintDeviceInfo(selector,var) \ + err = SPBGetDeviceInfo(mRefNum, selector, (Ptr) &var); \ + if (err) { \ + DBUG(("query %s failed\n", #selector )); \ + }\ + else { \ + DBUG(("query %s = 0x%x\n", #selector, var )); \ + } + + PrintDeviceInfo( siContinuous, tempS ); + PrintDeviceInfo( siAsync, tempS ); + PrintDeviceInfo( siNumberChannels, tempS ); + PrintDeviceInfo( siSampleSize, tempS ); + PrintDeviceInfo( siSampleRate, tempF ); + PrintDeviceInfo( siChannelAvailable, tempS ); + PrintDeviceInfo( siActiveChannels, tempL ); + PrintDeviceInfo( siDeviceBufferInfo, tempL ); + + err = SPBGetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL); + if (err == 0) DBUG(("%s = 0x%x\n", "siActiveChannels", tempL )); + /* Can we use this device? */ + err = SPBGetDeviceInfo(mRefNum, siAsync, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siAsync returned %d\n", err )); + goto error; + } + if( tempS == 0 ) goto useless; /* Does not support async recording so forget about it. */ + + err = SPBGetDeviceInfo(mRefNum, siChannelAvailable, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siChannelAvailable returned %d\n", err )); + goto error; + } + dev->maxInputChannels = tempS; + + /* Get sample rates supported. */ + err = SPBGetDeviceInfo(mRefNum, siSampleRateAvailable, (Ptr) &srinfo); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleRateAvailable returned %d\n", err )); + goto error; + } + + numRates = srinfo.numRates; + DBUG(("numRates = 0x%x\n", numRates )); + if( numRates == 0 ) + { + dev->numSampleRates = -1; + numRates = 2; + } + else + { + dev->numSampleRates = numRates; + } + dev->sampleRates = PaMac_GetSampleRatesFromHandle( numRates, (Handle) srinfo.rates ); + /* SPBGetDeviceInfo created the handle, but it's OUR job to release it. */ + DisposeHandle((Handle) srinfo.rates); + + /* Get size of device buffer. */ + err = SPBGetDeviceInfo(mRefNum, siDeviceBufferInfo, (Ptr) &tempL); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siDeviceBufferInfo returned %d\n", err )); + goto error; + } + ipad->pad_DeviceBufferSize = tempL; + DBUG(("siDeviceBufferInfo = %d\n", tempL )); + + /* Set format based on sample size. */ + err = SPBGetDeviceInfo(mRefNum, siSampleSize, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaMac_QueryInputDeviceInfo: SPBGetDeviceInfo siSampleSize returned %d\n", err )); + goto error; + } + switch( tempS ) + { + case 0x0020: + dev->nativeSampleFormats = paInt32; /* FIXME - warning, code probably won't support this! */ + break; + case 0x0010: + default: /* FIXME - What about other formats? */ + dev->nativeSampleFormats = paInt16; + break; + } + DBUG(("nativeSampleFormats = %d\n", dev->nativeSampleFormats )); + + /* Device name */ + len = deviceName[0] + 1; /* Get length of Pascal string */ + dev->name = (char *) malloc(len); /* MEM_010 */ + if( dev->name == NULL ) + { + result = paInsufficientMemory; + goto cleanup; + } + PToCString(deviceName, (char *) dev->name); + DBUG(("deviceName = %s\n", dev->name )); + result = (PaError) 1; + /* All done so close up device. */ +cleanup: + if( mRefNum ) SPBCloseDevice(mRefNum); + return result; + +error: + if( mRefNum ) SPBCloseDevice(mRefNum); + sPaHostError = err; + return paHostError; + +useless: + if( mRefNum ) SPBCloseDevice(mRefNum); + return (PaError) 0; +} + +/************************************************************************* +** Allocate a double array and fill it with listed sample rates. +*/ +static double * PaMac_GetSampleRatesFromHandle ( int numRates, Handle h ) +{ + OSErr err = noErr; + SInt8 hState; + int i; + UnsignedFixed *fixedRates; + double *rates = (double *) malloc( numRates * sizeof(double) ); /* MEM_011 */ + if( rates == NULL ) return NULL; + /* Save and restore handle state as suggested by TechNote at: + http://developer.apple.com/technotes/tn/tn1122.html + */ + hState = HGetState (h); + if (!(err = MemError ())) + { + HLock (h); + if (!(err = MemError ( ))) + { + fixedRates = (UInt32 *) *h; + for( i=0; i= Pa_CountDevices()) ) return NULL; + return &sDevices[id].pad_Info; +} +/*************************************************************************/ +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + return sDefaultInputDeviceID; +} + +/*************************************************************************/ +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + return sDefaultOutputDeviceID; +} + +/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ +static void PaMac_StartLoadCalculation( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + UnsignedWide widePad; + if( pahsc == NULL ) return; + /* Query system timer for usage analysis and to prevent overuse of CPU. */ + Microseconds( &widePad ); + pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad ); +} + +/****************************************************************************** +** Measure fractional CPU load based on real-time it took to calculate +** buffers worth of output. +*/ +/**************************************************************************/ +static void PaMac_EndLoadCalculation( internalPortAudioStream *past ) +{ + UnsignedWide widePad; + UInt64 currentCount; + long usecsElapsed; + double newUsage; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + + /* Measure CPU utilization during this callback. Note that this calculation + ** assumes that we had the processor the whole time. + */ +#define LOWPASS_COEFFICIENT_0 (0.95) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + Microseconds( &widePad ); + currentCount = UnsignedWideToUInt64( widePad ); + + usecsElapsed = (long) U64Subtract(currentCount, pahsc->pahsc_EntryCount); + + /* Use inverse because it is faster than the divide. */ + newUsage = usecsElapsed * pahsc->pahsc_InverseMicrosPerHostBuffer; + + past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + + (LOWPASS_COEFFICIENT_1 * newUsage); + +} + +/*********************************************************************** +** Called by Pa_StartStream() +*/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + pahsc->pahsc_IsRecording = 0; + pahsc->pahsc_StopRecording = 0; + pahsc->pahsc_InputMultiBuffer.nextWrite = 0; + pahsc->pahsc_InputMultiBuffer.nextRead = 0; + return PaMac_RecordNext( past ); +} + +/*********************************************************************** +** Called by Pa_StopStream(). +** May be called during error recovery or cleanup code +** so protect against NULL pointers. +*/ +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) +{ + int32 timeOutMsec; + PaError result = paNoError; + OSErr err = 0; + long mRefNum; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + + (void) abort; + + mRefNum = pahsc->pahsc_InputRefNum; + + DBUG(("PaHost_StopInput: mRefNum = %d\n", mRefNum )); + if( mRefNum ) + { + DBUG(("PaHost_StopInput: pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording )); + if( pahsc->pahsc_IsRecording ) + { + /* PLB20010420 - Fix TIMEOUT in record mode. */ + pahsc->pahsc_StopRecording = 1; /* Request that we stop recording. */ + err = SPBStopRecording(mRefNum); + DBUG(("PaHost_StopInput: then pahsc_IsRecording = %d\n", pahsc->pahsc_IsRecording )); + + /* Calculate timeOut longer than longest time it could take to play one buffer. */ + timeOutMsec = (int32) ((1500.0 * pahsc->pahsc_FramesPerHostBuffer) / past->past_SampleRate); + /* Keep querying sound channel until it is no longer busy playing. */ + while( !err && pahsc->pahsc_IsRecording && (timeOutMsec > 0)) + { + Pa_Sleep(20); + timeOutMsec -= 20; + } + if( timeOutMsec <= 0 ) + { + ERR_RPT(("PaHost_StopInput: timed out!\n")); + return paTimedOut; + } + } + } + if( err ) + { + sPaHostError = err; + result = paHostError; + } + + DBUG(("PaHost_StopInput: finished.\n", mRefNum )); + return result; +} + +/***********************************************************************/ +static void PaMac_InitSoundHeader( internalPortAudioStream *past, CmpSoundHeader *sndHeader ) +{ + sndHeader->numChannels = past->past_NumOutputChannels; + sndHeader->sampleRate = DoubleToUnsignedFixed(past->past_SampleRate); + sndHeader->loopStart = 0; + sndHeader->loopEnd = 0; + sndHeader->encode = cmpSH; + sndHeader->baseFrequency = kMiddleC; + sndHeader->markerChunk = nil; + sndHeader->futureUse2 = nil; + sndHeader->stateVars = nil; + sndHeader->leftOverSamples = nil; + sndHeader->compressionID = 0; + sndHeader->packetSize = 0; + sndHeader->snthID = 0; + sndHeader->sampleSize = 8 * sizeof(int16); // FIXME - might be 24 or 32 bits some day; + sndHeader->sampleArea[0] = 0; + sndHeader->format = kSoundNotCompressed; +} + +static void SetFramesDone( PaHostSoundControl *pahsc, PaTimestamp framesDone ) +{ + UnsignedWide now; + Microseconds( &now ); + pahsc->pahsc_NumFramesDone = framesDone; + pahsc->pahsc_WhenFramesDoneIncremented = UnsignedWideToUInt64( now ); +} + +/***********************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + SndCommand pauseCommand; + SndCommand resumeCommand; + int i; + OSErr error; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + if( pahsc->pahsc_Channel == NULL ) return paInternalError; + + past->past_StopSoon = 0; + past->past_IsActive = 1; + pahsc->pahsc_NumOutsQueued = 0; + pahsc->pahsc_NumOutsPlayed = 0; + + SetFramesDone( pahsc, 0.0 ); + + /* Pause channel so it does not do back ground processing while we are still filling the queue. */ + pauseCommand.cmd = pauseCmd; + pauseCommand.param1 = pauseCommand.param2 = 0; + error = SndDoCommand (pahsc->pahsc_Channel, &pauseCommand, true); + if (noErr != error) goto exit; + + /* Queue all of the buffers so we start off full. */ + for (i = 0; ipahsc_NumHostBuffers; i++) + { + PaMac_PlayNext( past, i ); + } + + /* Resume channel now that the queue is full. */ + resumeCommand.cmd = resumeCmd; + resumeCommand.param1 = resumeCommand.param2 = 0; + error = SndDoImmediate( pahsc->pahsc_Channel, &resumeCommand ); + if (noErr != error) goto exit; + + return paNoError; +exit: + past->past_IsActive = 0; + sPaHostError = error; + ERR_RPT(("Error in PaHost_StartOutput: SndDoCommand returned %d\n", error )); + return paHostError; +} + +/*******************************************************************/ +long PaHost_GetTotalBufferFrames( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + return (long) (pahsc->pahsc_NumHostBuffers * pahsc->pahsc_FramesPerHostBuffer); +} + +/*********************************************************************** +** Called by Pa_StopStream(). +** May be called during error recovery or cleanup code +** so protect against NULL pointers. +*/ +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +{ + int32 timeOutMsec; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + if( pahsc->pahsc_Channel == NULL ) return paNoError; + + DBUG(("PaHost_StopOutput()\n")); + if( past->past_IsActive == 0 ) return paNoError; + + /* Set flags for callback function to see. */ + if( abort ) past->past_StopNow = 1; + past->past_StopSoon = 1; + /* Calculate timeOut longer than longest time it could take to play all buffers. */ + timeOutMsec = (int32) ((1500.0 * PaHost_GetTotalBufferFrames( past )) / past->past_SampleRate); + /* Keep querying sound channel until it is no longer busy playing. */ + while( past->past_IsActive && (timeOutMsec > 0)) + { + Pa_Sleep(20); + timeOutMsec -= 20; + } + if( timeOutMsec <= 0 ) + { + ERR_RPT(("PaHost_StopOutput: timed out!\n")); + return paTimedOut; + } + else return paNoError; +} + +/***********************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + (void) past; /* Prevent unused variable warnings. */ + return paNoError; +} + +/***********************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + (void) past; /* Prevent unused variable warnings. */ + (void) abort; /* Prevent unused variable warnings. */ + return paNoError; +} +/***********************************************************************/ +PaError PaHost_StreamActive( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + return (PaError) ( past->past_IsActive + pahsc->pahsc_IsRecording ); +} +int Mac_IsVirtualMemoryOn( void ) +{ + long attr; + OSErr result = Gestalt( gestaltVMAttr, &attr ); + DBUG(("gestaltVMAttr : 0x%x\n", attr )); + return ((attr >> gestaltVMHasPagingControl ) & 1); +} + +/******************************************************************* +* Determine number of host Buffers +* and how many User Buffers we can put into each host buffer. +*/ +static void PaHost_CalcNumHostBuffers( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + int32 minNumBuffers; + int32 minFramesPerHostBuffer; + int32 minTotalFrames; + int32 userBuffersPerHostBuffer; + int32 framesPerHostBuffer; + int32 numHostBuffers; + + minFramesPerHostBuffer = pahsc->pahsc_MinFramesPerHostBuffer; + minFramesPerHostBuffer = (minFramesPerHostBuffer + 7) & ~7; + DBUG(("PaHost_CalcNumHostBuffers: minFramesPerHostBuffer = %d\n", minFramesPerHostBuffer )); + + /* Determine number of user buffers based on minimum latency. */ + /* PLB20020417 I used to call Pa_GetMinNumBuffers() which doesn't take into account the + ** variable minFramesPerHostBuffer. Now I call PaMac_GetMinNumBuffers() which will + ** gove lower latency when virtual memory is turned off. */ + /* minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); WRONG */ + minNumBuffers = PaMac_GetMinNumBuffers( minFramesPerHostBuffer, past->past_FramesPerUserBuffer, past->past_SampleRate ); + + past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers; + DBUG(("PaHost_CalcNumHostBuffers: min past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); + minTotalFrames = past->past_NumUserBuffers * past->past_FramesPerUserBuffer; + + /* We cannot make the buffers too small because they may not get serviced quickly enough. */ + if( (int32) past->past_FramesPerUserBuffer < minFramesPerHostBuffer ) + { + userBuffersPerHostBuffer = + (minFramesPerHostBuffer + past->past_FramesPerUserBuffer - 1) / + past->past_FramesPerUserBuffer; + } + else + { + userBuffersPerHostBuffer = 1; + } + framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer; + + /* Calculate number of host buffers needed. Round up to cover minTotalFrames. */ + numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; + /* Make sure we have enough host buffers. */ + if( numHostBuffers < PA_MIN_NUM_HOST_BUFFERS) + { + numHostBuffers = PA_MIN_NUM_HOST_BUFFERS; + } + else + { + /* If we have too many host buffers, try to put more user buffers in a host buffer. */ + while(numHostBuffers > PA_MAX_NUM_HOST_BUFFERS) + { + userBuffersPerHostBuffer += 1; + framesPerHostBuffer = past->past_FramesPerUserBuffer * userBuffersPerHostBuffer; + numHostBuffers = (minTotalFrames + framesPerHostBuffer - 1) / framesPerHostBuffer; + } + } + + pahsc->pahsc_UserBuffersPerHostBuffer = userBuffersPerHostBuffer; + pahsc->pahsc_FramesPerHostBuffer = framesPerHostBuffer; + pahsc->pahsc_NumHostBuffers = numHostBuffers; + DBUG(("PaHost_CalcNumHostBuffers: pahsc_UserBuffersPerHostBuffer = %d\n", pahsc->pahsc_UserBuffersPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_NumHostBuffers = %d\n", pahsc->pahsc_NumHostBuffers )); + DBUG(("PaHost_CalcNumHostBuffers: pahsc_FramesPerHostBuffer = %d\n", pahsc->pahsc_FramesPerHostBuffer )); + DBUG(("PaHost_CalcNumHostBuffers: past_NumUserBuffers = %d\n", past->past_NumUserBuffers )); +} + +/*******************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *past ) +{ + OSErr err; + PaError result = paHostError; + PaHostSoundControl *pahsc; + int i; + /* Allocate and initialize host data. */ + pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); + if( pahsc == NULL ) + { + return paInsufficientMemory; + } + past->past_DeviceData = (void *) pahsc; + + /* If recording, and virtual memory is turned on, then use bigger buffers to prevent glitches. */ + if( (past->past_NumInputChannels > 0) && Mac_IsVirtualMemoryOn() ) + { + pahsc->pahsc_MinFramesPerHostBuffer = MAC_VIRTUAL_FRAMES_PER_BUFFER; + } + else + { + pahsc->pahsc_MinFramesPerHostBuffer = MAC_PHYSICAL_FRAMES_PER_BUFFER; + } + + PaHost_CalcNumHostBuffers( past ); + + /* Setup constants for CPU load measurement. */ + pahsc->pahsc_InverseMicrosPerHostBuffer = past->past_SampleRate / (1000000.0 * pahsc->pahsc_FramesPerHostBuffer); + + /* ------------------ OUTPUT */ + if( past->past_NumOutputChannels > 0 ) + { + /* Create sound channel to which we can send commands. */ + pahsc->pahsc_Channel = 0L; + err = SndNewChannel(&pahsc->pahsc_Channel, sampledSynth, 0, nil); /* FIXME - use kUseOptionalOutputDevice if not default. */ + if(err != 0) + { + ERR_RPT(("Error in PaHost_OpenStream: SndNewChannel returned 0x%x\n", err )); + goto error; + } + + /* Install our callback function pointer straight into the sound channel structure */ + /* Use new CARBON name for callback procedure. */ +#if TARGET_API_MAC_CARBON + pahsc->pahsc_OutputCompletionProc = NewSndCallBackUPP(PaMac_OutputCompletionProc); +#else + pahsc->pahsc_OutputCompletionProc = NewSndCallBackProc(PaMac_OutputCompletionProc); +#endif + + pahsc->pahsc_Channel->callBack = pahsc->pahsc_OutputCompletionProc; + + pahsc->pahsc_BytesPerOutputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumOutputChannels * sizeof(int16); + for (i = 0; ipahsc_NumHostBuffers; i++) + { + char *buf = (char *)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputHostBuffer); + if (buf == NULL) + { + ERR_RPT(("Error in PaHost_OpenStream: could not allocate output buffer. Size = \n", pahsc->pahsc_BytesPerOutputHostBuffer )); + goto memerror; + } + + PaMac_InitSoundHeader( past, &pahsc->pahsc_SoundHeaders[i] ); + pahsc->pahsc_SoundHeaders[i].samplePtr = buf; + pahsc->pahsc_SoundHeaders[i].numFrames = (unsigned long) pahsc->pahsc_FramesPerHostBuffer; + + } + } +#ifdef SUPPORT_AUDIO_CAPTURE + /* ------------------ INPUT */ + /* Use double buffer scheme that matches output. */ + if( past->past_NumInputChannels > 0 ) + { + int16 tempS; + long tempL; + Fixed tempF; + long mRefNum; + unsigned char noname = 0; /* FIXME - use real device names. */ +#if TARGET_API_MAC_CARBON + pahsc->pahsc_InputCompletionProc = NewSICompletionUPP((SICompletionProcPtr)PaMac_InputCompletionProc); +#else + pahsc->pahsc_InputCompletionProc = NewSICompletionProc((ProcPtr)PaMac_InputCompletionProc); +#endif + pahsc->pahsc_BytesPerInputHostBuffer = pahsc->pahsc_FramesPerHostBuffer * past->past_NumInputChannels * sizeof(int16); + for (i = 0; ipahsc_NumHostBuffers; i++) + { + char *buf = (char *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputHostBuffer); + if ( buf == NULL ) + { + ERR_RPT(("PaHost_OpenStream: could not allocate input buffer. Size = \n", pahsc->pahsc_BytesPerInputHostBuffer )); + goto memerror; + } + pahsc->pahsc_InputMultiBuffer.buffers[i] = buf; + } + pahsc->pahsc_InputMultiBuffer.numBuffers = pahsc->pahsc_NumHostBuffers; + + err = SPBOpenDevice( (const unsigned char *) &noname, siWritePermission, &mRefNum); /* FIXME - use name so we get selected device */ + // FIXME err = SPBOpenDevice( (const unsigned char *) sDevices[past->past_InputDeviceID].pad_Info.name, siWritePermission, &mRefNum); + if (err) goto error; + pahsc->pahsc_InputRefNum = mRefNum; + DBUG(("PaHost_OpenStream: mRefNum = %d\n", mRefNum )); + + /* Set input device characteristics. */ + tempS = 1; + err = SPBSetDeviceInfo(mRefNum, siContinuous, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siContinuous returned %d\n", err )); + goto error; + } + + tempL = 0x03; + err = SPBSetDeviceInfo(mRefNum, siActiveChannels, (Ptr) &tempL); + if (err) + { + DBUG(("PaHost_OpenStream: setting siActiveChannels returned 0x%x. Error ignored.\n", err )); + } + + /* PLB20010908 - Use requested number of input channels. Thanks Dominic Mazzoni. */ + tempS = past->past_NumInputChannels; + err = SPBSetDeviceInfo(mRefNum, siNumberChannels, (Ptr) &tempS); + if (err) + { + ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siNumberChannels returned %d\n", err )); + goto error; + } + + tempF = ((unsigned long)past->past_SampleRate) << 16; + err = SPBSetDeviceInfo(mRefNum, siSampleRate, (Ptr) &tempF); + if (err) + { + ERR_RPT(("Error in PaHost_OpenStream: SPBSetDeviceInfo siSampleRate returned %d\n", err )); + goto error; + } + + /* Setup record-parameter block */ + pahsc->pahsc_InputParams.inRefNum = mRefNum; + pahsc->pahsc_InputParams.milliseconds = 0; // not used + pahsc->pahsc_InputParams.completionRoutine = pahsc->pahsc_InputCompletionProc; + pahsc->pahsc_InputParams.interruptRoutine = 0; + pahsc->pahsc_InputParams.userLong = (long) past; + pahsc->pahsc_InputParams.unused1 = 0; + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + DBUG(("PaHost_OpenStream: complete.\n")); + return paNoError; + +error: + PaHost_CloseStream( past ); + ERR_RPT(("PaHost_OpenStream: sPaHostError = 0x%x.\n", err )); + sPaHostError = err; + return paHostError; + +memerror: + PaHost_CloseStream( past ); + return paInsufficientMemory; +} + +/*********************************************************************** +** Called by Pa_CloseStream(). +** May be called during error recovery or cleanup code +** so protect against NULL pointers. +*/ +PaError PaHost_CloseStream( internalPortAudioStream *past ) +{ + PaError result = paNoError; + OSErr err = 0; + int i; + PaHostSoundControl *pahsc; + + DBUG(("PaHost_CloseStream( 0x%x )\n", past )); + + if( past == NULL ) return paBadStreamPtr; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + + if( past->past_NumOutputChannels > 0 ) + { + /* TRUE means flush now instead of waiting for quietCmd to be processed. */ + if( pahsc->pahsc_Channel != NULL ) SndDisposeChannel(pahsc->pahsc_Channel, TRUE); + { + for (i = 0; ipahsc_NumHostBuffers; i++) + { + Ptr p = (Ptr) pahsc->pahsc_SoundHeaders[i].samplePtr; + if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerOutputHostBuffer ); + } + } + } + + if( past->past_NumInputChannels > 0 ) + { + if( pahsc->pahsc_InputRefNum ) + { + err = SPBCloseDevice(pahsc->pahsc_InputRefNum); + pahsc->pahsc_InputRefNum = 0; + if( err ) + { + sPaHostError = err; + result = paHostError; + } + } + { + for (i = 0; ipahsc_InputMultiBuffer.numBuffers; i++) + { + Ptr p = (Ptr) pahsc->pahsc_InputMultiBuffer.buffers[i]; + if( p != NULL ) PaHost_FreeFastMemory( p, pahsc->pahsc_BytesPerInputHostBuffer ); + } + } + } + + past->past_DeviceData = NULL; + PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); + + DBUG(("PaHost_CloseStream: complete.\n", past )); + return result; +} +/*************************************************************************/ +int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) +{ +/* We use the MAC_VIRTUAL_FRAMES_PER_BUFFER because we might be recording. +** This routine doesn't have enough information to determine the best value +** and is being depracated. */ + return PaMac_GetMinNumBuffers( MAC_VIRTUAL_FRAMES_PER_BUFFER, framesPerUserBuffer, sampleRate ); +} +/*************************************************************************/ +static int PaMac_GetMinNumBuffers( int minFramesPerHostBuffer, int framesPerUserBuffer, double sampleRate ) +{ + int minUserPerHost = ( minFramesPerHostBuffer + framesPerUserBuffer - 1) / framesPerUserBuffer; + int numBufs = PA_MIN_NUM_HOST_BUFFERS * minUserPerHost; + if( numBufs < PA_MIN_NUM_HOST_BUFFERS ) numBufs = PA_MIN_NUM_HOST_BUFFERS; + (void) sampleRate; + return numBufs; +} + +/*************************************************************************/ +void Pa_Sleep( int32 msec ) +{ + EventRecord event; + int32 sleepTime, endTime; + /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */ + sleepTime = ((msec * 60) + 999) / 1000; + if( sleepTime < 1 ) sleepTime = 1; + endTime = TickCount() + sleepTime; + do + { + DBUGX(("Sleep for %d ticks.\n", sleepTime )); + /* Use WaitNextEvent() to sleep without getting events. */ + /* PLB20010907 - Pass unused event to WaitNextEvent instead of NULL to prevent + * Mac OSX crash. Thanks Dominic Mazzoni. */ + WaitNextEvent( 0, &event, sleepTime, NULL ); + sleepTime = endTime - TickCount(); + } + while( sleepTime > 0 ); +} +/*************************************************************************/ +int32 Pa_GetHostError( void ) +{ + int32 err = sPaHostError; + sPaHostError = 0; + return err; +} + +/************************************************************************* + * Allocate memory that can be accessed in real-time. + * This may need to be held in physical memory so that it is not + * paged to virtual memory. + * This call MUST be balanced with a call to PaHost_FreeFastMemory(). + */ +void *PaHost_AllocateFastMemory( long numBytes ) +{ + void *addr = NewPtrClear( numBytes ); + if( (addr == NULL) || (MemError () != 0) ) return NULL; + +#if (TARGET_API_MAC_CARBON == 0) + if( HoldMemory( addr, numBytes ) != noErr ) + { + DisposePtr( (Ptr) addr ); + return NULL; + } +#endif + return addr; +} + +/************************************************************************* + * Free memory that could be accessed in real-time. + * This call MUST be balanced with a call to PaHost_AllocateFastMemory(). + */ +void PaHost_FreeFastMemory( void *addr, long numBytes ) +{ + if( addr == NULL ) return; +#if TARGET_API_MAC_CARBON + (void) numBytes; +#else + UnholdMemory( addr, numBytes ); +#endif + DisposePtr( (Ptr) addr ); +} + +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + PaTimestamp framesDone1; + PaTimestamp framesDone2; + UInt64 whenIncremented; + UnsignedWide now; + UInt64 now64; + long microsElapsed; + long framesElapsed; + + PaHostSoundControl *pahsc; + internalPortAudioStream *past = (internalPortAudioStream *) stream; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + +/* Capture information from audio thread. + * We have to be careful that we don't get interrupted in the middle. + * So we grab the pahsc_NumFramesDone twice and make sure it didn't change. + */ + do + { + framesDone1 = pahsc->pahsc_NumFramesDone; + whenIncremented = pahsc->pahsc_WhenFramesDoneIncremented; + framesDone2 = pahsc->pahsc_NumFramesDone; + } while( framesDone1 != framesDone2 ); + + /* Calculate how many microseconds have elapsed and convert to frames. */ + Microseconds( &now ); + now64 = UnsignedWideToUInt64( now ); + microsElapsed = U64Subtract( now64, whenIncremented ); + framesElapsed = microsElapsed * past->past_SampleRate * 0.000001; + + return framesDone1 + framesElapsed; +} + +/************************************************************************** +** Callback for Input, SPBRecord() +*/ +int gRecordCounter = 0; +int gPlayCounter = 0; +pascal void PaMac_InputCompletionProc(SPBPtr recParams) +{ + PaError result = paNoError; + int finished = 1; + internalPortAudioStream *past; + PaHostSoundControl *pahsc; + + gRecordCounter += 1; /* debug hack to see if engine running */ + + /* Get our PA data from Mac structure. */ + past = (internalPortAudioStream *) recParams->userLong; + if( past == NULL ) return; + + if( past->past_Magic != PA_MAGIC ) + { + AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, past", (long) past ); + AddTraceMessage("PaMac_InputCompletionProc: bad MAGIC, magic", (long) past->past_Magic ); + goto error; + } + pahsc = (PaHostSoundControl *) past->past_DeviceData; + past->past_NumCallbacks += 1; + + /* Have we been asked to stop recording? */ + if( (recParams->error == abortErr) || pahsc->pahsc_StopRecording ) goto error; + + /* If there are no output channels, then we need to call the user callback function from here. + * Otherwise we will call the user code during the output completion routine. + */ + if(past->past_NumOutputChannels == 0) + { + SetFramesDone( pahsc, + pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer ); + result = PaMac_CallUserLoop( past, NULL ); + } + + /* Did user code ask us to stop? If not, issue another recording request. */ + if( (result == paNoError) && (pahsc->pahsc_StopRecording == 0) ) + { + result = PaMac_RecordNext( past ); + if( result != paNoError ) pahsc->pahsc_IsRecording = 0; + } + else goto error; + + return; + +error: + pahsc->pahsc_IsRecording = 0; + pahsc->pahsc_StopRecording = 0; + return; +} + +/*********************************************************************** +** Called by either input or output completion proc. +** Grabs input data if any present, and calls PA conversion code, +** that in turn calls user code. +*/ +static PaError PaMac_CallUserLoop( internalPortAudioStream *past, int16 *outPtr ) +{ + PaError result = paNoError; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + int16 *inPtr = NULL; + int i; + + + /* Advance read index for sound input FIFO here, independantly of record/write process. */ + if(past->past_NumInputChannels > 0) + { + if( MultiBuffer_IsReadable( &pahsc->pahsc_InputMultiBuffer ) ) + { + inPtr = (int16 *) MultiBuffer_GetNextReadBuffer( &pahsc->pahsc_InputMultiBuffer ); + MultiBuffer_AdvanceReadIndex( &pahsc->pahsc_InputMultiBuffer ); + } + } + + /* Call user code enough times to fill buffer. */ + if( (inPtr != NULL) || (outPtr != NULL) ) + { + PaMac_StartLoadCalculation( past ); /* CPU usage */ + +#ifdef PA_MAX_USAGE_ALLOWED + /* If CPU usage exceeds limit, skip user callback to prevent hanging CPU. */ + if( past->past_Usage > PA_MAX_USAGE_ALLOWED ) + { + past->past_FrameCount += (PaTimestamp) pahsc->pahsc_FramesPerHostBuffer; + } + else +#endif + { + + for( i=0; ipahsc_UserBuffersPerHostBuffer; i++ ) + { + result = (PaError) Pa_CallConvertInt16( past, inPtr, outPtr ); + if( result != 0) + { + /* Recording might be in another process, so tell it to stop with a flag. */ + pahsc->pahsc_StopRecording = pahsc->pahsc_IsRecording; + break; + } + /* Advance sample pointers. */ + if(inPtr != NULL) inPtr += past->past_FramesPerUserBuffer * past->past_NumInputChannels; + if(outPtr != NULL) outPtr += past->past_FramesPerUserBuffer * past->past_NumOutputChannels; + } + } + + PaMac_EndLoadCalculation( past ); + } + return result; +} + +/*********************************************************************** +** Setup next recording buffer in FIFO and issue recording request to Snd Input Manager. +*/ +static PaError PaMac_RecordNext( internalPortAudioStream *past ) +{ + PaError result = paNoError; + OSErr err; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + /* Get pointer to next buffer to record into. */ + pahsc->pahsc_InputParams.bufferPtr = MultiBuffer_GetNextWriteBuffer( &pahsc->pahsc_InputMultiBuffer ); + + /* Advance write index if there is room. Otherwise keep writing same buffer. */ + if( MultiBuffer_IsWriteable( &pahsc->pahsc_InputMultiBuffer ) ) + { + MultiBuffer_AdvanceWriteIndex( &pahsc->pahsc_InputMultiBuffer ); + } + + AddTraceMessage("PaMac_RecordNext: bufferPtr", (long) pahsc->pahsc_InputParams.bufferPtr ); + AddTraceMessage("PaMac_RecordNext: nextWrite", pahsc->pahsc_InputMultiBuffer.nextWrite ); + + /* Setup parameters and issue an asynchronous recording request. */ + pahsc->pahsc_InputParams.bufferLength = pahsc->pahsc_BytesPerInputHostBuffer; + pahsc->pahsc_InputParams.count = pahsc->pahsc_BytesPerInputHostBuffer; + err = SPBRecord(&pahsc->pahsc_InputParams, true); + if( err ) + { + AddTraceMessage("PaMac_RecordNext: SPBRecord error ", err ); + sPaHostError = err; + result = paHostError; + } + else + { + pahsc->pahsc_IsRecording = 1; + } + return result; +} + +/************************************************************************** +** Callback for Output Playback() +** Return negative error, 0 to continue, 1 to stop. +*/ +long PaMac_FillNextOutputBuffer( internalPortAudioStream *past, int index ) +{ + PaHostSoundControl *pahsc; + long result = 0; + int finished = 1; + char *outPtr; + + gPlayCounter += 1; /* debug hack */ + + past->past_NumCallbacks += 1; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return -1; + /* Are we nested?! */ + if( pahsc->pahsc_IfInsideCallback ) return 0; + pahsc->pahsc_IfInsideCallback = 1; + /* Get pointer to buffer to fill. */ + outPtr = pahsc->pahsc_SoundHeaders[index].samplePtr; + /* Combine with any sound input, and call user callback. */ + result = PaMac_CallUserLoop( past, (int16 *) outPtr ); + + pahsc->pahsc_IfInsideCallback = 0; + return result; +} + +/************************************************************************************* +** Called by SoundManager when ready for another buffer. +*/ +static pascal void PaMac_OutputCompletionProc (SndChannelPtr theChannel, SndCommand * theCallBackCmd) +{ + internalPortAudioStream *past; + PaHostSoundControl *pahsc; + (void) theChannel; + (void) theCallBackCmd; + + /* Get our data from Mac structure. */ + past = (internalPortAudioStream *) theCallBackCmd->param2; + if( past == NULL ) return; + + pahsc = (PaHostSoundControl *) past->past_DeviceData; + pahsc->pahsc_NumOutsPlayed += 1; + + SetFramesDone( pahsc, + pahsc->pahsc_NumFramesDone + pahsc->pahsc_FramesPerHostBuffer ); + + PaMac_BackgroundManager( past, theCallBackCmd->param1 ); +} + +/*******************************************************************/ +static PaError PaMac_BackgroundManager( internalPortAudioStream *past, int index ) +{ + PaError result = paNoError; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + /* Has someone asked us to abort by calling Pa_AbortStream()? */ + if( past->past_StopNow ) + { + SndCommand command; + /* Clear the queue of any pending commands. */ + command.cmd = flushCmd; + command.param1 = command.param2 = 0; + SndDoImmediate( pahsc->pahsc_Channel, &command ); + /* Then stop currently playing buffer, if any. */ + command.cmd = quietCmd; + SndDoImmediate( pahsc->pahsc_Channel, &command ); + past->past_IsActive = 0; + } + /* Has someone asked us to stop by calling Pa_StopStream() + * OR has a user callback returned '1' to indicate finished. + */ + else if( past->past_StopSoon ) + { + if( (pahsc->pahsc_NumOutsQueued - pahsc->pahsc_NumOutsPlayed) <= 0 ) + { + past->past_IsActive = 0; /* We're finally done. */ + } + } + else + { + PaMac_PlayNext( past, index ); + } + return result; +} + +/************************************************************************************* +** Fill next buffer with sound and queue it for playback. +*/ +static void PaMac_PlayNext ( internalPortAudioStream *past, int index ) +{ + OSErr error; + long result; + SndCommand playCmd; + SndCommand callbackCmd; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + + /* If this was the last buffer, or abort requested, then just be done. */ + if ( past->past_StopSoon ) goto done; + + /* Load buffer with sound. */ + result = PaMac_FillNextOutputBuffer ( past, index ); + if( result > 0 ) past->past_StopSoon = 1; /* Stop generating audio but wait until buffers play. */ + else if( result < 0 ) goto done; + + /* Play the next buffer. */ + playCmd.cmd = bufferCmd; + playCmd.param1 = 0; + playCmd.param2 = (long) &pahsc->pahsc_SoundHeaders[ index ]; + error = SndDoCommand (pahsc->pahsc_Channel, &playCmd, true ); + if( error != noErr ) goto gotError; + + /* Ask for a callback when it is done. */ + callbackCmd.cmd = callBackCmd; + callbackCmd.param1 = index; + callbackCmd.param2 = (long)past; + error = SndDoCommand (pahsc->pahsc_Channel, &callbackCmd, true ); + if( error != noErr ) goto gotError; + pahsc->pahsc_NumOutsQueued += 1; + + return; + +gotError: + sPaHostError = error; +done: + return; +} diff --git a/pd/portaudio/pa_sgi/Makefile b/pd/portaudio/pa_sgi/Makefile new file mode 100644 index 00000000..6548c6c5 --- /dev/null +++ b/pd/portaudio/pa_sgi/Makefile @@ -0,0 +1,51 @@ +# Make PortAudio for Silicon Graphics IRIX (6.2) +# Pieter suurmond, september 23, 2001. (pa_sgi sub-version #0.21 for PA v15.) +# Based on SGI-specific sproc()-method to spawn children, not on POSIX-threads. + +# Instead of "-lpthread", as with linux, +# just the audio- and math-library for SGI: +LIBS = -laudio -lm + +CDEFINES = -I../pa_common + +# Possible CFLAGS with MIPSpro compiler are: -32, -o32, -n32, -64, +# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging. +# And use for example -O2 or -O3 for better optimization: +CFLAGS = -O2 +PASRC = ../pa_common/pa_lib.c pa_sgi.c +PAINC = ../pa_common/portaudio.h + +# Tests that work (SGI Indy with R5000 @ 180MHz running IRIX 6.2). +TESTC = $(PASRC) ../pa_tests/patest_record.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_many.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_latency.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_longsine.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_saw.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_wire.c # OK +#TESTC = $(PASRC) ../pa_tests/pa_devs.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_sine.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_sine8.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_pink.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_clip.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_stop.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_dither.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_sync.c # BEEPS and delta's, no crashes anymore. + +# Tests that were not yet performed. +#TESTC = $(PASRC) ../pa_tests/paqa_devs.c # test?? +#TESTC = $(PASRC) ../pa_tests/paqa_errs.c # test?? +#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c # test?? + +TESTH = $(PAINC) + +all: patest + +# "cc" for the MIPSpro compiler, may be changed to "gcc": +patest: $(TESTC) $(TESTH) Makefile + cc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest + +run: patest + ./patest + diff --git a/pd/portaudio/pa_sgi/pa_sgi.c b/pd/portaudio/pa_sgi/pa_sgi.c new file mode 100644 index 00000000..4b91f02c --- /dev/null +++ b/pd/portaudio/pa_sgi/pa_sgi.c @@ -0,0 +1,999 @@ +/* + * $Id: pa_sgi.c,v 1.2 2002/05/04 20:38:42 philburk Exp $ + * PortAudio Portable Real-Time Audio Library. Copyright (c) 1999-2001 Phil Burk. + * Latest Version at: http://www.portaudio.com + * + * Silicon Graphics IRIX implementation by Pieter Suurmond, september 23, 2001. + * pa_sgi-sub-version number 0.21, for PortAudio v15. + * This implementation uses the sproc()-method, not the POSIX method. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * +Modfication History: + 8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2. + 8/17/2001 - v15 pa_sgi sub-version #0.04 (unstable alpha release) Sent to Phil & Ross. + 9/23/2001 - #0.21 Many fixes and changes: POLLIN for input, not POLLOUT. + Open and close ALports in the audio-process-thread. + Proper semaphore communication now. + Hopefully stable enough now (for beta release?). +TODO: + - Test under IRIX 6.5. + - Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion), + and maybe also the other natively supported formats? (might increase performance) + - The minimal number of buffers setting... I do not yet fully understand it.. I now just take *4. +REFERENCES: + - IRIX 6.2 man pages regarding SGI AL library. + - IRIS Digital MediaProgramming Guide (online books and man-pages come + with IRIX 6.2 and may not be publically available on the internet). +*/ + +#include /* Standard libraries. */ +#include + +#include "../pa_common/portaudio.h" /* BETTER PATH !!!???? Portaudio headers. */ +#include "../pa_common/pa_host.h" +#include "../pa_common/pa_trace.h" + +#include +#include +#include +#include /* For schedctl(NDPRI, NDPHIMIN). */ +#include /* fcntl.h needed. */ +#include /* For streams, ioctl(), etc. */ +#include +#include +#include /* System specific (IRIX 6.2). */ + +/*--------------------------------------------*/ +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +#define MAX_CHARS_DEVNAME (16) /* Was 32 in OSS (20 for AL but "in"/"out" is concat. */ +#define MAX_SAMPLE_RATES (8) /* Known from SGI AL there are 7 (was 10 in OSS v15). */ + +typedef struct internalPortAudioDevice /* IRIX specific device info: */ +{ + PaDeviceID /* NEW: */ pad_DeviceID; /* THIS "ID" IS NEW HERE (Pieter)! */ + long pad_ALdevice; /* SGI-number! */ + double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */ + char pad_DeviceName[MAX_CHARS_DEVNAME+1]; /* +1 for \0, one more than OSS. */ + PaDeviceInfo pad_Info; /* pad_Info (v15) contains: */ + /* int structVersion; */ + /* const char* name; */ + /* int maxInputChannels, maxOutputChannels; */ + /* int numSampleRates; Num rates, or -1 if range supprtd. */ + /* const double* sampleRates; Array of supported sample rates, */ + /* PaSampleFormat nativeSampleFormats; or {min,max} if range supported. */ + struct internalPortAudioDevice* pad_Next; /* Singly linked list, (NULL=end). */ +} internalPortAudioDevice; + +typedef struct PaHostSoundControl /* Structure to contain all SGI IRIX specific data. */ +{ + ALconfig pahsc_ALconfigIN, /* IRIX-audio-library-datatype. Configuration */ + pahsc_ALconfigOUT; /* stucts separate for input and output ports. */ + ALport pahsc_ALportIN, /* IRIX-audio-library-datatype. ALports can only be */ + pahsc_ALportOUT; /* unidirectional, so we sometimes need 2 of them. */ + int pahsc_threadPID; /* Sproc()-result, written by PaHost_StartEngine(). */ + short *pahsc_NativeInputBuffer, /* Allocated here, in this file, if necessary. */ + *pahsc_NativeOutputBuffer; + unsigned int pahsc_BytesPerInputBuffer, /* Native buffer sizes in bytes, really needed here */ + pahsc_BytesPerOutputBuffer; /* to free FAST memory, if buffs were alloctd FAST. */ + unsigned int pahsc_SamplesPerInputBuffer, /* These amounts are needed again and again in the */ + pahsc_SamplesPerOutputBuffer; /* audio-thread (don't need to be kept globally). */ + struct itimerval pahsc_EntryTime, /* For measuring CPU utilization (same as linux). */ + pahsc_LastExitTime; + long pahsc_InsideCountSum, + pahsc_TotalCountSum; +} PaHostSoundControl; + +/*-------------------------------------------------------- Shared Data -------------------------------*/ +static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */ +static int sPaHostError = 0; /* Maybe more than one process writing errs!? */ +usema_t *SendSema, /* These variables are shared between the */ + *RcvSema; /* audio handling process and main process. */ +/*--------------------------*/ +long Pa_GetHostError(void) +{ + return (long)sPaHostError; +} + +/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/ +/* (copied from source pa_linux_oss/pa_linux_oss.c) */ +static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +{ + struct itimerval itimer; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; +/* Query system timer for usage analysis and to prevent overuse of CPU. */ + getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime ); +} + +static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB ) +{ + long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec; + long usecs = secs * 1000000; + usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec); + return usecs; +} + +static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +{ + struct itimerval currentTime; + long insideCount; + long totalCount; /* Measure CPU utilization during this callback. */ + +#define LOWPASS_COEFFICIENT_0 (0.95) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if (pahsc == NULL) + return; + if (getitimer( ITIMER_REAL, ¤tTime ) == 0 ) + { + if (past->past_IfLastExitValid) + { + insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, ¤tTime ); + pahsc->pahsc_InsideCountSum += insideCount; + totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, ¤tTime ); + pahsc->pahsc_TotalCountSum += totalCount; + /* DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */ + /* Low pass filter the result because sometimes we get called several times in a row. */ + /* That can cause the TotalCount to be very low which can cause the usage to appear */ + /* unnaturally high. So we must filter numerator and denominator separately!!! */ + if (pahsc->pahsc_InsideCountSum > 0) + { + past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) + + (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum)); + past->past_AverageTotalCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) + + (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum)); + past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount; + pahsc->pahsc_InsideCountSum = 0; + pahsc->pahsc_TotalCountSum = 0; + } + } + past->past_IfLastExitValid = 1; + } + pahsc->pahsc_LastExitTime.it_value.tv_sec = 100; + pahsc->pahsc_LastExitTime.it_value.tv_usec = 0; + setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL ); + past->past_IfLastExitValid = 1; +} /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/ + + +/*--------------------------------------------------------------------------------------*/ +PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */ +{ /* call to report via ERR_RPT(), yields a PaError-num. */ + const char* a = "SGI AL "; /* (Not absolutely sure errno came from THIS thread! */ + switch(oserror()) /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */ + { + case AL_BAD_OUT_OF_MEM: + ERR_RPT(("%sout of memory.\n", a)); + return paInsufficientMemory; /* Known PaError. */ + case AL_BAD_CONFIG: + ERR_RPT(("%sconfiguration invalid or NULL.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_CHANNELS: + ERR_RPT(("%schannels not 1,2 or 4.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_NO_PORTS: + ERR_RPT(("%sout of audio ports.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_DEVICE: + ERR_RPT(("%swrong device number.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_DEVICE_ACCESS: + ERR_RPT(("%swrong device access.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_DIRECTION: + ERR_RPT(("%sinvalid direction.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_SAMPFMT: + ERR_RPT(("%sdoesn't accept sampleformat.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_FLOATMAX: + ERR_RPT(("%smax float value is zero.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_WIDTH: + ERR_RPT(("%sunsupported samplewidth.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_QSIZE: + ERR_RPT(("%sinvalid queue size.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_PVBUFFER: + ERR_RPT(("%sPVbuffer null.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_BUFFERLENGTH_NEG: + ERR_RPT(("%snegative bufferlength.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_BUFFERLENGTH_ODD: + ERR_RPT(("%sodd bufferlength.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_PARAM: + ERR_RPT(("%sparameter not valid for device.\n", a)); + return paHostError; /* Generic PaError. */ + default: + ERR_RPT(("%sunknown error.\n", a)); + return paHostError; /* Generic PaError. */ + } +} + +/*------------------------------------------------------------------------------------------*/ +/* Tries to set various rates and formats and fill in the device info structure. */ +static PaError Pa_sgiQueryDevice(long ALdev, /* (AL_DEFAULT_DEVICE) */ + PaDeviceID id, /* (DefaultI|ODeviceID()) */ + char* name, /* (for example "SGI AL") */ + internalPortAudioDevice* pad) /* Result written to pad. */ +{ + int format; + long min, max; /* To catch hardware characteristics. */ + ALseterrorhandler(0); /* 0 = turn off the default error handler. */ + /*--------------------------------------------------------------------------------------*/ + pad->pad_ALdevice = ALdev; /* Set the AL device number. */ + pad->pad_DeviceID = id; /* Set the PA device number. */ + if (strlen(name) > MAX_CHARS_DEVNAME) /* MAX_CHARS defined above. */ + { + ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name)); + return paHostError; + } + strcpy(pad->pad_DeviceName, name); /* Write name-string. */ + pad->pad_Info.name = pad->pad_DeviceName; /* Set pointer,..hmmm. */ + /*--------------------------------- natively supported sample formats: -----------------*/ + pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */ + /* Then also choose other CallConvertXX()! */ + /*--------------------------------- number of available i/o channels: ------------------*/ + if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max)) + return translateSGIerror(); + pad->pad_Info.maxInputChannels = max; + DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels)) + if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max)) + return translateSGIerror(); + pad->pad_Info.maxOutputChannels = max; + DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels)) + /*--------------------------------- supported samplerates: ----------------------*/ + pad->pad_Info.numSampleRates = 7; + pad->pad_Info.sampleRates = pad->pad_SampleRates; + pad->pad_SampleRates[0] = (double)AL_RATE_8000; /* long -> double. */ + pad->pad_SampleRates[1] = (double)AL_RATE_11025; + pad->pad_SampleRates[2] = (double)AL_RATE_16000; + pad->pad_SampleRates[3] = (double)AL_RATE_22050; + pad->pad_SampleRates[4] = (double)AL_RATE_32000; + pad->pad_SampleRates[5] = (double)AL_RATE_44100; + pad->pad_SampleRates[6] = (double)AL_RATE_48000; + if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max)) /* Ask INPUT rate-max. */ + return translateSGIerror(); /* double -> long. */ + if (max != (long)(0.5 + pad->pad_SampleRates[6])) /* FP-compare not recommndd. */ + goto weird; + if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max. */ + return translateSGIerror(); + if (max != (long)(0.5 + pad->pad_SampleRates[6])) + { +weird: ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max)); + return paHostError; /* Or make it a warning and just carry on... */ + } + /*-------------------------------------------------------------------------------*/ + return paNoError; +} + + +/*--------------------------------------------------------------------------------*/ +int Pa_CountDevices() /* Name of this function suggests it only counts and */ +{ /* is NOT destructive, it however resets whole PA ! */ + int numDevices = 0; /* Let 's not do that here. */ + internalPortAudioDevice* currentDevice = sDeviceList; /* COPY GLOBAL VAR. */ +#if 0 /* Remains from linux_oss v15: Pa_Initialize(), on */ + if (!currentDevice) /* its turn, calls PaHost_Init() via file pa_lib.c. */ + Pa_Initialize(); /* Isn't that a bit too 'rude'? Don't be too */ +#endif /* friendly to clients that forgot to initialize PA. */ + while (currentDevice) /* Slower but more elegant than the sNumDevices-way: */ + { + numDevices++; + currentDevice = currentDevice->pad_Next; + } + return numDevices; +} + +/*-------------------------------------------------------------------------------*/ +static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id) +{ + int numDevices = 0; + internalPortAudioDevice *res = (internalPortAudioDevice*)NULL; + internalPortAudioDevice *pad = sDeviceList; /* COPY GLOBAL VAR. */ + while (pad) /* pad may be NULL, that's ok, return 0. */ + { /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.) */ + if (pad->pad_DeviceID == id) /* This the device we were looking for? */ + res = pad; /* But keep on(!) counting so we don't */ + numDevices++; /* have to call Pa_CountDevices() later. */ + pad = pad->pad_Next; /* Advance to the next device or NULL. */ + } /* No assumptions about order of ID's in */ + if (!res) /* the list. */ + ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id)); + if ((id < 0) || (id >= numDevices)) + { + ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id)); +#if 1 /* Be strict, even when found, */ + res = (internalPortAudioDevice*)NULL; /* do not accept illegal ID's. */ +#endif + } + return res; +} + +/*----------------------------------------------------------------------*/ +const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id) +{ + PaDeviceInfo* res = (PaDeviceInfo*)NULL; + internalPortAudioDevice* pad = Pa_GetInternalDevice(id); /* Call. */ + if (pad) + res = &pad->pad_Info; /* Not finding the specified ID is not */ + if (!res) /* the same as &pad->pad_Info == NULL. */ + ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id)); + return res; /* So (maybe) a second/third ERR_RPT(). */ +} + +/*------------------------------------------------*/ +PaDeviceID Pa_GetDefaultInputDeviceID(void) +{ + return 0; /* 0 is the default device ID. */ +} +/*------------------------------------------------*/ +PaDeviceID Pa_GetDefaultOutputDeviceID(void) +{ + return 0; +} + +/*-------------------------------------------------------------------------------------------------*/ +/* Build linked a list with all the available audio devices on this SGI machine (only 1 for now). */ +PaError PaHost_Init(void) /* Called by Pa_Initialize() from pa_lib.c. */ +{ + internalPortAudioDevice* pad; + PaError r = paNoError; + int audioLibFileID; /* To test for the presence of audio. */ + + if (sDeviceList) /* Allow re-init, only warn, no error. */ + { + ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n")); + return r; + } + /*------------- ADD THE SGI DEFAULT DEVICE TO THE LIST: ---------------------------------------*/ + audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY); /* Try to open Indigo style audio */ + if (audioLibFileID < 0) /* IO port. On failure, machine */ + { /* has no audio ability. */ + ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n")); + return paHostError; + } + close(audioLibFileID); /* Allocate fast mem to hold device info. */ + pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice)); + if (pad == NULL) + return paInsufficientMemory; + memset(pad, 0, sizeof(internalPortAudioDevice)); /* "pad->pad_Next = NULL" is more elegant. */ + r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE, /* Set AL device num (AL_DEFAULT_DEVICE). */ + Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */ + "AL default", /* A suitable name. */ + pad); /* Write args and queried info into pad. */ + if (r != paNoError) + { + ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r)); + PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); /* sDeviceList still NULL ! */ + } + else + sDeviceList = pad; /* First element in linked list. pad->pad_Next already NULL. */ + /*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LINKED LIST: -------------------*/ + /*---------------------------------------------------------------------------------------------*/ + return r; +} + +/*--------------------------------------------------------------------------------------------*/ +#define MIN(a,b) ((a)<(b)?(a):(b)) /* MIN()-function is used below. */ +#define kPollSEMA 0 /* To index the pollfd-array, reads nicer than just */ +#define kPollOUT 1 /* numbers. */ +#define kPollIN 2 +void Pa_SgiAudioProcess(void *v) /* This function is sproc-ed by PaHost_StartEngine() */ +{ /* as a separate thread. (Argument must be void*). */ + short evtLoop; /* Reset by parent indirectly, or at local errors. */ + PaError result; + struct pollfd PollFD[3]; /* To catch kPollSEMA-, kPollOUT- and kPollIN-events. */ + internalPortAudioStream *past = (internalPortAudioStream*)v; /* Copy void-ptr-argument.*/ + PaHostSoundControl *pahsc; + short inputEvent, outputEvent, /* .revents members are of type short. */ + semaEvent = 0; + DBUG(("Entering sproc-thread.\n")); + if (!past) + { + sPaHostError = paInternalError; /* Or paBadStreamPtr ? */ + ERR_RPT(("argument NULL!\n")); + goto skip; + } + pahsc = (PaHostSoundControl*)past->past_DeviceData; + if (!pahsc) + { + sPaHostError = paInternalError; /* The only way is to signal error to shared area?! */ + ERR_RPT(("past_DeviceData NULL!\n")); + goto skip; /* Sproc-ed threads MAY NOT RETURN paInternalError. */ + } + /*----------------------------- open AL-ports here, after sproc(): -----------------------*/ + if (past->past_NumInputChannels > 0) /* Open input port. */ + { + pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", pahsc->pahsc_ALconfigIN); + if (!pahsc->pahsc_ALportIN) + { + ERR_RPT(("Failed to open AL input port.\n")); + sPaHostError = paInternalError; + goto skip; + } + DBUG(("Opened %d input channel(s).\n", past->past_NumInputChannels)); + } + if (past->past_NumOutputChannels > 0) /* Open output port. */ + { + pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", pahsc->pahsc_ALconfigOUT); + if (!pahsc->pahsc_ALportOUT) + { + ERR_RPT(("Failed to open AL output port.\n")); + sPaHostError = paInternalError; /* Assume pahsc_ALconfigs are the */ + goto skip; /* same for IN and OUT in case */ + } /* both ports are opened (bidir). */ + DBUG(("Opened %d output channel(s).\n", past->past_NumOutputChannels)); + } + /*-----------------------------------------------------------------------*/ + past->past_IsActive = 1; /* Wasn't this already done by the calling parent?! */ + PollFD[kPollIN].fd = ALgetfd(pahsc->pahsc_ALportIN); /* ALgetfd returns -1 on failures */ + PollFD[kPollIN].events = POLLIN; /* such as ALport not there. */ + PollFD[kPollOUT].fd = ALgetfd(pahsc->pahsc_ALportOUT); + PollFD[kPollOUT].events = POLLOUT; /* .events = POLLOUT is OK. */ + schedctl(NDPRI, NDPHIMIN); /* Sets non-degrading priority for this process. */ + PollFD[kPollSEMA].fd = usopenpollsema(SendSema, 0777); /* To communicate with parent. */ + PollFD[kPollSEMA].events = POLLIN; /* .events = POLLIN is OK. */ + uspsema(SendSema); /* Blocks until ... MUST be here, this uspsema(). */ + evtLoop = ((past->past_StopNow | past->past_StopSoon) == 0); + while (evtLoop) + { + /*---------------------------- SET FILLPOINTS AND WAIT UNTIL SOMETHING HAPPENS: ----------*/ + if (pahsc->pahsc_NativeInputBuffer) /* Then pahsc_ALportIN should also be there! */ + /* For input port, fill point is number of locations in the sample queue that must be */ + /* filled in order to trigger a return from select(). (or poll()) */ + /* Notice IRIX docs mention number of samples as argument, not number of sampleframes.*/ + if (ALsetfillpoint(pahsc->pahsc_ALportIN, pahsc->pahsc_SamplesPerInputBuffer)) + { /* Same amount as transferred per time. */ + ERR_RPT(("ALsetfillpoint() for ALportIN failed.\n")); + sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */ + goto skip; + } + if (pahsc->pahsc_NativeOutputBuffer) /* Then pahsc_ALportOUT should also be there! */ + /* For output port, fill point is number of locations that must be free in order to */ + /* wake up from select(). (or poll()) */ + if (ALsetfillpoint(pahsc->pahsc_ALportOUT, pahsc->pahsc_SamplesPerOutputBuffer)) + { + ERR_RPT(("ALsetfillpoint() for ALportOUT failed.\n")); + sPaHostError = paInternalError; /* (Using exit(-1) would be a bit rude.) */ + goto skip; + } /* poll() with timeout=-1 makes it block until a requested */ + poll(PollFD, 3, -1); /* event occurs or until call is interrupted. If fd-value in */ + /* array <0, events is ignored and revents is set to 0. */ + /*---------------------------- MESSAGE-EVENT FROM PARENT THREAD: -------------------------*/ + semaEvent = PollFD[kPollSEMA].revents & POLLIN; + if (semaEvent) + { + if (past->past_StopSoon) + evtLoop = 0; + if (past->past_StopNow) + goto skip; + } + /*------------------------------------- FILLED-EVENT FROM INPUT BUFFER: --------------------------*/ + inputEvent = PollFD[kPollIN].revents & POLLIN; + if (inputEvent) /* Don't need to check (pahsc->pahsc_NativeInputBuffer): */ + { /* if buffer was not there, ALport not there, no events! */ + if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_NativeInputBuffer, + pahsc->pahsc_SamplesPerInputBuffer)) + { /* Here again: number of samples instead of number of frames. */ + ERR_RPT(("ALreadsamps() failed.\n")); + sPaHostError = paInternalError; + goto skip; + } + } + outputEvent = PollFD[kPollOUT].revents & POLLOUT; + /*------------------------------------- USER-CALLBACK-ROUTINE: -----------------------------------*/ + if (inputEvent | outputEvent) /* (Bitwise is ok.) */ + { /* To be sure we that really DID input-transfer or gonna DO output-transfer, and that it is */ + /* not just "sema"- (i.e. user)-event, or some other system-event that awakened the poll(). */ + Pa_StartUsageCalculation(past); /* Convert 16 bit native data to */ + result = Pa_CallConvertInt16(past, /* user data and call user routine. */ + pahsc->pahsc_NativeInputBuffer, + pahsc->pahsc_NativeOutputBuffer); + Pa_EndUsageCalculation(past); + if (result) + { + DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result)); + goto skip; /* This is apparently NOT an error! */ + } /* Just letting the userCallBack stop us. */ + } + /*------------------------------------- FREE-EVENT FROM OUTPUT BUFFER: ---------------------------*/ + if (outputEvent) /* Don't need to check (pahsc->pahsc_NativeOutputBuffer) */ + { /* because if filedescriptor not there, no event for it. */ + if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_NativeOutputBuffer, + pahsc->pahsc_SamplesPerOutputBuffer)) + { + ERR_RPT(("ALwritesamps() failed.\n")); /* Better use SEMAS for messaging back to parent! */ + sPaHostError = paInternalError; + goto skip; + } + } + } +skip: + /*------------------------------- close AL-ports ----------------------------*/ + if (pahsc->pahsc_ALportIN) + { + if (ALcloseport(pahsc->pahsc_ALportIN)) + translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */ + else /* But go on anyway... to release other stuff... */ + pahsc->pahsc_ALportIN = (ALport)0; + } + if (pahsc->pahsc_ALportOUT) + { + if (ALcloseport(pahsc->pahsc_ALportOUT)) + translateSGIerror(); + else + pahsc->pahsc_ALportOUT = (ALport)0; + } + past->past_IsActive = 0; + if (semaEvent) + { + uspsema(SendSema); /* StopEngine() was still waiting for this acknowledgement. */ + usvsema(RcvSema); /* (semaEvent initialized with 0.) */ + } + DBUG(("Leaving sproc-thread.\n")); +} + + +#define kALinternalQueuesizeFact 4L /* Internal queue 4 times as large as transferSize. */ + /* Used below, twice: for input and for output. */ +/*--------------------------------------------------------------------------------------*/ +PaError PaHost_OpenStream(internalPortAudioStream *past) +{ + PaError result = paNoError; + PaHostSoundControl *pahsc; + unsigned int minNumBuffers; + internalPortAudioDevice *padIN, *padOUT; /* For looking up native AL-numbers. */ + long pvbuf[8]; /* To get/set hardware configs. */ + long sr; + DBUG(("PaHost_OpenStream() called.\n")); /* Alloc FASTMEM and init host data. */ + if (!past) + { + ERR_RPT(("Streampointer NULL!\n")); + result = paBadStreamPtr; goto done; + } + pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); + if (pahsc == NULL) + { + ERR_RPT(("FAST Memory allocation failed.\n")); /* Pass trough some ERR_RPT-exit- */ + result = paInsufficientMemory; goto done; /* code (nothing will be freed). */ + } + memset(pahsc, 0, sizeof(PaHostSoundControl)); + pahsc->pahsc_threadPID = -1; /* Should pahsc_threadPID be inited to */ + past->past_DeviceData = (void*)pahsc; /* -1 instead of 0 ?? */ + /*------------------------------------------ Manipulate hardware if necessary and allowed: --*/ + ALseterrorhandler(0); /* 0 = turn off the default error handler. */ + pvbuf[0] = AL_INPUT_RATE; + pvbuf[2] = AL_INPUT_COUNT; + pvbuf[4] = AL_OUTPUT_RATE; /* TO FIX: rates may be logically, not always in Hz! */ + pvbuf[6] = AL_OUTPUT_COUNT; + sr = (long)(past->past_SampleRate + 0.5); /* Common for input and output :-) */ + /*---------------------------------------------------- SET INPUT CONFIGURATION: ------------------------*/ + if (past->past_NumInputChannels > 0) /* We need to lookup the corre- */ + { /* sponding native AL-number(s). */ + /*--------------------------------------------------- Allocate native buffers: --------*/ + pahsc->pahsc_SamplesPerInputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */ + past->past_NumInputChannels; /* audio-thread. */ + pahsc->pahsc_BytesPerInputBuffer = pahsc->pahsc_SamplesPerInputBuffer * sizeof(short); + pahsc->pahsc_NativeInputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputBuffer); + if (!pahsc->pahsc_NativeInputBuffer) + { + ERR_RPT(("Fast memory allocation failed (in).\n")); + result = paInsufficientMemory; + goto done; + } + padIN = Pa_GetInternalDevice(past->past_InputDeviceID); + if (!padIN) + { + ERR_RPT(("Pa_GetInternalDevice() for input failed.\n")); + result = paHostError; + goto done; + } + if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */ + goto sgiError; /* the same AL-device, the AL-library might */ + if (pvbuf[1] != sr) /* contain more than AL_DEFAULT_DEVICE in */ + { /* Rate different from current harware-rate? the future. Therefore 2 seperate queries. */ + if (pvbuf[3] > 0) /* Means, there's other clients using AL-input-ports */ + { + ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \ +another process is currently using input at %ld kHz.\n", sr, pvbuf[1])); + result = paHostError; + goto done; + } + pvbuf[1] = sr; /* Then set input-rate. */ + if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2)) + goto sgiError; /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */ + } /* IS NOT CHECKED, AT LEAST NOT BY ME, WITHIN THIS FILE! Does PA do? */ + pahsc->pahsc_ALconfigIN = ALnewconfig(); /* Released at PaHost_CloseStream(). */ + if (pahsc->pahsc_ALconfigIN == (ALconfig)0) + goto sgiError; + if (ALsetsampfmt(pahsc->pahsc_ALconfigIN, AL_SAMPFMT_TWOSCOMP))/* Choose paInt16 as native i/o-format. */ + goto sgiError; + if (ALsetwidth (pahsc->pahsc_ALconfigIN, AL_SAMPLE_16)) /* Only meaningful when sample format for */ + goto sgiError; /* config is set to two's complement format. */ + /************************ Future versions might (dynamically) switch to 32-bit floats? ******* + if (ALsetsampfmt(pahsc_ALconfigIN, AL_SAMPFMT_FLOAT)) (Then also call another CallConvert-func.) + goto sgiError; + if (ALsetfloatmax (pahsc_ALconfigIN, 1.0)) Only meaningful when sample format for config + goto sgiError; is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */ + /*--------- Set internal AL queuesize (in samples) -------------------------------*/ + if (ALsetqueuesize(pahsc->pahsc_ALconfigIN, (long)pahsc->pahsc_SamplesPerInputBuffer * + (long)kALinternalQueuesizeFact)) + goto sgiError; /* Or should we use past_NumUserBuffers here? */ + /* Do 4 timea, using 2 times may give glitches. */ + if (ALsetchannels (pahsc->pahsc_ALconfigIN, (long)(past->past_NumInputChannels))) + goto sgiError; /* Returns 0 on success, -1 on failure. */ + } + /*---------------------------------------------------- SET OUTPUT CONFIGURATION: ------------------------*/ + if (past->past_NumOutputChannels > 0) /* CARE: padOUT/IN may NOT be NULL if Channels <= 0! */ + { /* We use padOUT/IN later on, or at least 1 of both. */ + pahsc->pahsc_SamplesPerOutputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */ + past->past_NumOutputChannels; /* audio-thread. */ + pahsc->pahsc_BytesPerOutputBuffer = pahsc->pahsc_SamplesPerOutputBuffer * sizeof(short); + + pahsc->pahsc_NativeOutputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputBuffer); + if (!pahsc->pahsc_NativeOutputBuffer) + { + ERR_RPT(("Fast memory allocation failed (out).\n")); + result = paInsufficientMemory; + goto done; + } + padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID); + if (!padOUT) + { + ERR_RPT(("Pa_GetInternalDevice() for output failed.\n")); + result = paHostError; + goto done; + } + if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4)) + goto sgiError; + if (pvbuf[5] != sr) + { /* Output needed and rate different from current harware-rate. */ + if (pvbuf[7] > 0) /* Means, there's other clients using AL-output-ports */ + { + ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \ +another process is currently using output at %ld kHz.\n", sr, pvbuf[5])); + result = paHostError; + goto done; /* Will free again the inputbuffer */ + } /* that was just created above. */ + pvbuf[5] = sr; /* Then set output-rate. */ + if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2)) + goto sgiError; + } + pahsc->pahsc_ALconfigOUT = ALnewconfig(); /* Released at PaHost_CloseStream(). */ + if (pahsc->pahsc_ALconfigOUT == (ALconfig)0) + goto sgiError; + if (ALsetsampfmt(pahsc->pahsc_ALconfigOUT, AL_SAMPFMT_TWOSCOMP)) /* Choose paInt16 as native i/o-format. */ + goto sgiError; + if (ALsetwidth (pahsc->pahsc_ALconfigOUT, AL_SAMPLE_16)) /* Only meaningful when sample format for */ + goto sgiError; /* config is set to two's complement format. */ + /************************************ Future versions might (dynamically) switch to 32-bit floats? *******/ + if (ALsetqueuesize(pahsc->pahsc_ALconfigOUT, (long)pahsc->pahsc_SamplesPerOutputBuffer * + (long)kALinternalQueuesizeFact)) + goto sgiError; /* Or should we use past_NumUserBuffers here?*/ + if (ALsetchannels (pahsc->pahsc_ALconfigOUT, (long)(past->past_NumOutputChannels))) + goto sgiError; + } + /*---------- ?? --------------------*/ + /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */ + minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer, past->past_SampleRate); + past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ? + minNumBuffers : past->past_NumUserBuffers; /* I don't yet use past_NumUserBuffers */ + /*----------------------------------------------- TEST DEVICE ID's: --------------------*/ + if ((past->past_OutputDeviceID != past->past_InputDeviceID) && /* Who SETS these devive-numbers? */ + (past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0)) + { + ERR_RPT(("Cannot setup bidirectional stream between different devices.\n")); + result = paHostError; + goto done; + } + goto done; /* (no errors occured) */ +sgiError: + result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */ +done: + if (result != paNoError) + PaHost_CloseStream(past); /* Frees memory (only if really allocated!). */ + return result; +} + +/*-----------------------------------------------------*/ +PaError PaHost_StartOutput(internalPortAudioStream *past) +{ + return paNoError; /* Hmm, not implemented yet? */ +} +PaError PaHost_StartInput(internalPortAudioStream *past) +{ + return paNoError; +} + +/*------------------------------------------------------------------------------*/ +PaError PaHost_StartEngine(internalPortAudioStream *past) +{ + PaHostSoundControl *pahsc; + usptr_t *arena; + if (!past) /* Test argument. */ + { + ERR_RPT(("PaHost_StartEngine(NULL)!\n")); + return paBadStreamPtr; + } + pahsc = (PaHostSoundControl*)past->past_DeviceData; + if (!pahsc) + { + ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData = NULL!\n")); + return paHostError; + } + past->past_StopSoon = 0; /* Assume SGI ALport is already opened! */ + past->past_StopNow = 0; /* Why don't we check pahsc for NULL? */ + past->past_IsActive = 1; + + /* Although the pthread_create() function, as well as , may be */ + /* available in IRIX, use sproc() on SGI to create audio-background-thread. */ + /* (Linux/oss uses pthread_create() instead of __clone() because: */ + /* - pthread_create also works for other UNIX systems like Solaris, */ + /* - Java HotSpot VM crashes in pthread_setcanceltype() using __clone().) */ + + usconfig(CONF_ARENATYPE, US_SHAREDONLY); /* (From SGI-AL-examples, file */ + arena = usinit(tmpnam(0)); /* motifexample.c, function */ + SendSema = usnewpollsema(arena, 0); /* InitializeAudioProcess().) */ + RcvSema = usnewsema(arena, 1); /* 1= common mutual exclusion semaphore, where 1 and only 1 process + will be permitted through a semaphore at a time. Values > 1 + imply that up to val resources may be simultaneously used, but requests + for more than val resources cause the calling process to block until a + resource comes free (by a process holding a resource performing a + usvsema(). IS THIS usnewsema() TOO PLATFORM SPECIFIC? */ + prctl(PR_SETEXITSIG, 0); /* No not (void*)9, but 0, which doesn't kill the parent! */ + /* PR_SETEXITSIG controls whether all members of a share group will be + signaled if any one of them leaves the share group (either via exit() + or exec()). If 2nd arg, interpreted as an int is 0, then normal IRIX + process termination rules apply, namely that the parent is sent a + SIGCLD upon death of child, but no indication of death of parent is + given. If the second argument is a valid signal number then if any + member of a share group leaves the share group, a signal is + sent to ALL surviving members of the share group. */ + /* SPAWN AUDIO-CHILD: */ + pahsc->pahsc_threadPID = sproc(Pa_SgiAudioProcess, /* Returns process ID of */ + PR_SALL, /* new process, or -1. */ + (void*)past); /* Pass past as optional */ /* IS THIS SAFE, will past never */ + if (pahsc->pahsc_threadPID == -1) /* third void-ptr-arg. */ /* be moved around in memory???? */ + { + ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n")); + sPaHostError = oserror(); /* Pass native error-number to shared area. */ + return paHostError; /* But return the generic error-number. */ + } + return paNoError; /* Hmmm, errno may come from other threads in same group! */ +} /* ("man sproc" in IRIX6.2 to read about _SGI_MP_SOURCE.) */ + +/*------------------------------------------------------------------------------*/ +PaError PaHost_StopEngine(internalPortAudioStream *past, int abort) +{ + int hres; + long timeOut; + PaError result = paNoError; + PaHostSoundControl *pahsc; + + DBUG(("PaHost_StopEngine() called.\n")); + if (!past) + return paBadStreamPtr; + pahsc = (PaHostSoundControl*)past->past_DeviceData; + /* Prevent from doing this twice!! */ + if ((!pahsc) || /* Some tests call this CLOSE twice!! */ + (!past->past_IsActive) || + past->past_StopSoon || past->past_StopNow) + return result; /* paNoError (already stopped, no err?). */ + past->past_StopSoon = 1; /* Tell background thread to stop generating */ + if (abort) /* more and to let current data play out. If */ + past->past_StopNow = 1; /* aborting, tell backgrnd thread to stop NOW! */ + /*---- USE SEMAPHORE LOCK TO COMMUNICATE: -----*/ + usvsema(SendSema); /* Increments count associated with SendSema. */ + /* Wait for the response. */ + uspsema(RcvSema); /* Decrements count of previously allocated */ + /* semaphore specified by RcvSema. */ + while (past->past_IsActive) /* REALLY WAIT. */ + { + /* DBUG(("wait 1 ms for audio-thread to stop.\n")); */ + Pa_Sleep(1); + } + +#if 0 /* We don't need to KILL(), just COMMUNICATE and be patient... */ + if (pahsc->pahsc_threadPID != -1) /* Did we really init it to -1 somewhere? */ + { + DBUG(("PaHost_StopEngine() is about to kill(SIGKILL) audio-thread.\n")); + if (kill(pahsc->pahsc_threadPID, SIGKILL)) /* Or SIGTERM or SIGQUIT(core) */ + { /* Returns -1 in case of error. */ + result = paHostError; + sPaHostError = oserror(); /* Hmmm, other threads may also write here! */ + ERR_RPT(("PaHost_StopEngine() failed to kill audio-thread.\n")); + } + else + pahsc->pahsc_threadPID = -1; /* Notify that we've killed this thread. */ + } +#endif + past->past_IsActive = 0; /* Even when kill() failed and pahsc_threadPID still there??? */ + return result; +} + +/*---------------------------------------------------------------*/ +PaError PaHost_StopOutput(internalPortAudioStream *past, int abort) +{ + return paNoError; /* Not implemented yet? */ +} +PaError PaHost_StopInput(internalPortAudioStream *past, int abort ) +{ + return paNoError; +} + +/*******************************************************************/ +PaError PaHost_CloseStream(internalPortAudioStream *past) +{ + PaHostSoundControl *pahsc; + PaError result = paNoError; + + DBUG(("PaHost_CloseStream() called.\n")); + if (!past) + return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if (!pahsc) /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */ + return result; /* This test prevents from freeing NULL-pointers. */ + + if (pahsc->pahsc_ALconfigIN) + { /* Release configuration structs, only if allocated. */ + ALfreeconfig(pahsc->pahsc_ALconfigIN); + pahsc->pahsc_ALconfigIN = NULL; + } + if (pahsc->pahsc_ALconfigOUT) + { + ALfreeconfig(pahsc->pahsc_ALconfigOUT); /* (Al-ports were already closed by audioProcess). */ + pahsc->pahsc_ALconfigOUT = NULL; + } + if (pahsc->pahsc_NativeInputBuffer) + { + PaHost_FreeFastMemory(pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_BytesPerInputBuffer); + pahsc->pahsc_NativeInputBuffer = NULL; + } + if (pahsc->pahsc_NativeOutputBuffer) + { + PaHost_FreeFastMemory(pahsc->pahsc_NativeOutputBuffer, pahsc->pahsc_BytesPerOutputBuffer); + pahsc->pahsc_NativeOutputBuffer = NULL; + } + PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl)); + past->past_DeviceData = NULL; /* PaHost_OpenStream() allocated FAST MEM. */ + return result; +} + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** set PA_MIN_LATENCY_MSEC=200 +** in the AUTOEXEC.BAT file and reboot. +** If the environment variable is not set, then the latency will be +** determined based on the OS. Windows NT has higher latency than Win95. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") + +int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ) +{ + return 2; +} +/* Hmmm, the note above isn't appropriate for SGI I'm afraid... */ +/* Do we HAVE to do it this way under IRIX???.... */ +/*--------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------*/ +PaError PaHost_Term(void) /* Frees all of the linked audio-devices. */ +{ /* Called by Pa_Terminate() from pa_lib.c. */ + internalPortAudioDevice *pad = sDeviceList, + *nxt; + while (pad) + { + DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName)); + nxt = pad->pad_Next; + PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); + pad = nxt; /* PaHost_Init allocated this FAST MEM.*/ + } + sDeviceList = (internalPortAudioDevice*)NULL; + return 0; /* Got rid of sNumDevices=0; */ +} + +/***********************************************************************/ +void Pa_Sleep( long msec ) /* Sleep requested number of milliseconds. */ +{ +#if 0 + struct timeval timeout; + timeout.tv_sec = msec / 1000; + timeout.tv_usec = (msec % 1000) * 1000; + select(0, NULL, NULL, NULL, &timeout); +#else + long usecs = msec * 1000; + usleep( usecs ); +#endif +} + +/*---------------------------------------------------------------------------------------*/ +/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */ +/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */ +/* a call to PaHost_FreeFastMemory(). */ +void *PaHost_AllocateFastMemory(long numBytes) +{ + void *addr = malloc(numBytes); /* mpin() reads into memory all pages over the given */ + if (addr) /* range and locks the pages into memory. A counter */ + { /* is incremented each time the page is locked. The */ + if (mpin(addr, numBytes)) /* superuser can lock as many pages as it wishes, */ + { /* others are limited to the configurable PLOCK_MA. */ + ERR_RPT(("PaHost_AllocateFastMemory() failed to mpin() memory.\n")); +#if 1 + free(addr); /* You MAY cut out these 2 lines to be less strict, */ + addr = NULL; /* you then only get the warning but PA goes on... */ +#endif /* Only problem then may be corresponding munpin() */ + } /* call at PaHost_FreeFastMemory(), below. */ + memset(addr, 0, numBytes); /* Locks established with mlock are not inherited by */ + } /* a child process after a fork. Furthermore, IRIX- */ + return addr; /* man-pages warn against mixing both mpin and mlock */ +} /* in 1 piece of code, so stick to mpin()/munpin() ! */ + + +/*---------------------------------------------------------------------------------------*/ +/* Free memory that could be accessed in real-time. This call MUST be balanced with a */ +/* call to PaHost_AllocateFastMemory(). */ +void PaHost_FreeFastMemory(void *addr, long numBytes) +{ + if (addr) + { + if (munpin(addr, numBytes)) /* Will munpin() fail when it was never mpinned? */ + ERR_RPT(("WARNING: PaHost_FreeFastMemory() failed to munpin() memory.\n")); + free(addr); /* But go on, try to release it, just warn... */ + } +} + +/*----------------------------------------------------------*/ +PaError PaHost_StreamActive( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + if (past == NULL) + return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if (pahsc == NULL) + return paInternalError; + return (PaError)(past->past_IsActive != 0); +} + +/*-------------------------------------------------------------------*/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + internalPortAudioStream *past = (internalPortAudioStream *) stream; +/* FIXME - return actual frames played, not frames generated. +** Need to query the output device somehow. +*/ + return past->past_FrameCount; +} diff --git a/pd/portaudio/pa_sgi/pthread-Makefile b/pd/portaudio/pa_sgi/pthread-Makefile new file mode 100644 index 00000000..527677a8 --- /dev/null +++ b/pd/portaudio/pa_sgi/pthread-Makefile @@ -0,0 +1,52 @@ +# Make PortAudio for Silicon Graphics IRIX (6.2) +# Pieter suurmond, september 22, 2001. (v15 pa_sgi sub-version #0.18) + +# pthread, math (as with linux) and SGI audio library: +# SGI-books say -lpthread should be the last on the line. +LIBS = -lm -laudio -lpthread + +CDEFINES = -I../pa_common + +# Possible CFLAGS with MIPSpro compiler are: -32, -o32, -n32, -64, +# -mips1, -mips2, -mips3, -mips4, etc. Use -g, -g2, -g3 for debugging. +# And use for example -O2 or -O3 for better optimization: +CFLAGS = -O2 +PASRC = ../pa_common/pa_lib.c pa_sgi.c +PAINC = ../pa_common/portaudio.h + +# Tests that work (SGI Indy with R5000 @ 180MHz running IRIX 6.2). +#TESTC = $(PASRC) ../pa_tests/patest_record.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_many.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_latency.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_longsine.c # OK but needs more than 4 buffers to do without glitches. +TESTC = $(PASRC) ../pa_tests/patest_saw.c # Seems OK (does it gracefully exit?). +#TESTC = $(PASRC) ../pa_tests/patest_wire.c # OK +#TESTC = $(PASRC) ../pa_tests/pa_devs.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_sine.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_sine8.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_leftright.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_pink.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_clip.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_stop.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_dither.c # OK +#TESTC = $(PASRC) ../pa_tests/patest_sync.c # BEEPS and delta's, no crashes anymore. + +# Tests that do not yet work. +#TESTC = $(PASRC) ../pa_tests/paqa_devs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 1 output channel(s) on AL default +#TESTC = $(PASRC) ../pa_tests/paqa_errs.c # Heavy crash with core-dump after PaHost_OpenStream: opening 2 output channel(s) on AL default +#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c # THIS FUZZ CRASHED MY WHOLE IRIX SYSTEM after "ENTER"! :-( + # PROCESS IN ITSELF RUNS OK, WITH LARGER BUFFSIZES THOUGH. + # OH MAYBE IT WAS BECAUSE DEBUGGING WAS ON??? + # ANYWAY, I'M NOT GONNA RUN THAT AGAIN... WAY TOO DANGEROUS! +TESTH = $(PAINC) + +all: patest + +# "cc" for the MIPSpro compiler, may be changed to "gcc": +patest: $(TESTC) $(TESTH) Makefile + cc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest + +run: patest + ./patest + diff --git a/pd/portaudio/pa_sgi/pthread-pa_sgi.c b/pd/portaudio/pa_sgi/pthread-pa_sgi.c new file mode 100644 index 00000000..e9ab273c --- /dev/null +++ b/pd/portaudio/pa_sgi/pthread-pa_sgi.c @@ -0,0 +1,908 @@ +/* + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * SGI IRIX implementation by Pieter Suurmond, september 22, 2001 (#0.18). + * + * Copyright (c) 1999-2001 Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* +Modfication History: + 8/12/2001 - Pieter Suurmond - took the v15 pa_linux_oss.c file and started to adapt for IRIX 6.2. + 8/17/2001 - alpha release with IRIX sproc()-method, may sometimes let IRIX6.2 crash at closing audiostream. + 9/22/2001 - #0.18 pthread starts to work a bit: + BUT UNDER IRIX6.2, I DON'T GET IT TO WORK REALLY CORRECTLY, + this POSIX-attempt, + DON'T USE THIS FILE FOR RELIABLE OPERATION, IT IS HERE JUST + FOR DOCUMENTATION/ARCHIVE... OR FOR ANYONE WHO WANTS TO FIX...... +TODO: + - Test under IRIX 6.5. + - Dynamically switch to 32 bit float as native format when appropriate (let SGI do the conversion), + and maybe also the other natively supported formats? (might increase performance) + - Not sure whether CPU UTILIZATION MEASUREMENT (from OSS/linux) really works. Changed nothing yet, + seems ok, but I've not yet tested it thoroughly. (maybe utilization-code may be made _unix_common_ then?) + - The minimal number of buffers setting... I do not yet fully understand it.. I now just take *4. +REFERENCES: + - IRIX 6.2 man pages regarding SGI AL library. + - IRIS Digital MediaProgramming Guide (online books as well as man-pages come with IRIX 6.2 and + may not be publically available on the internet). +*/ + +#include /* Standard libraries. */ +#include + +#include "../pa_common/portaudio.h" /* Portaudio headers. */ +#include "../pa_common/pa_host.h" +#include "../pa_common/pa_trace.h" + +/* +#include +#include +#include Not needed +#include +#include +#include +#include Needed? +#include +#include sched_param struct and related functions + used in setting thread priorities. +#include Some POSIX constants such as _POSIX_THREAD_THREADS_MAX +*/ + +#include /* Pthreads are supported by IRIX 6.2 after */ + /* patches 1361, 1367, and 1429 are applied. */ + +#include /* fcntl.h needed for "O_RDONLY". */ +#include /* For usleep() and constants used when calling sysconf() */ + /* to query POSIX limits (see the sysconf(3) ref. page. */ + +#include /* SGI-specific audio library. */ + + +/*--------------------------------------------*/ +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) PRINT(x) +#define DBUGX(x) /* PRINT(x) */ + +#define MAX_CHARS_DEVNAME (16) /* Was 32 in OSS (20 for AL but "in"/"out" is concat. */ +#define MAX_SAMPLE_RATES (8) /* Known from SGI AL there are 7 (was 10 in OSS v15). */ + +typedef struct internalPortAudioDevice /* IRIX specific device info: */ +{ + PaDeviceID /* NEW: */ pad_DeviceID; /* THIS "ID" IS NEW HERE (Pieter)! */ + long pad_ALdevice; /* SGI-number! */ + double pad_SampleRates[MAX_SAMPLE_RATES]; /* for pointing to from pad_Info */ + char pad_DeviceName[MAX_CHARS_DEVNAME+1]; /* +1 for \0, one more than OSS. */ + PaDeviceInfo pad_Info; /* pad_Info (v15) contains: */ + /* int structVersion; */ + /* const char* name; */ + /* int maxInputChannels, maxOutputChannels; */ + /* int numSampleRates; Num rates, or -1 if range supprtd. */ + /* const double* sampleRates; Array of supported sample rates, */ + /* PaSampleFormat nativeSampleFormats; or {min,max} if range supported. */ + struct internalPortAudioDevice* pad_Next; /* Singly linked list, (NULL=end). */ +} internalPortAudioDevice; + +typedef struct PaHostSoundControl /* Structure to contain all SGI IRIX specific data. */ +{ + ALport pahsc_ALportIN, /* IRIX-audio-library-datatype. ALports can only be */ + pahsc_ALportOUT; /* unidirectional, so we sometimes need 2 of them. */ + pthread_t pahsc_ThreadPID; + short *pahsc_NativeInputBuffer, /* Allocated here, in this file, if necessary. */ + *pahsc_NativeOutputBuffer; + unsigned int pahsc_BytesPerInputBuffer, /* Native buffer sizes in bytes, really needed here */ + pahsc_BytesPerOutputBuffer; /* to free FAST memory, if buffs were alloctd FAST. */ + unsigned int pahsc_SamplesPerInputBuffer, /* These amounts are needed again and again in the */ + pahsc_SamplesPerOutputBuffer; /* audio-thread (don't need to be kept globally). */ + struct itimerval pahsc_EntryTime, /* For measuring CPU utilization (same as linux). */ + pahsc_LastExitTime; + long pahsc_InsideCountSum, + pahsc_TotalCountSum; +} PaHostSoundControl; + +/*----------------------------- Shared Data ------------------------------------------------------*/ +static internalPortAudioDevice* sDeviceList = NULL; /* FIXME - put Mutex around this shared data. */ +static int sPaHostError = 0; /* Maybe more than one process writing errs!? */ + +long Pa_GetHostError(void) +{ + return (long)sPaHostError; +} + +/*----------------------------- BEGIN CPU UTILIZATION MEASUREMENT -----------------*/ +/* (copied from source pa_linux_oss/pa_linux_oss.c) */ +static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +{ + struct itimerval itimer; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; +/* Query system timer for usage analysis and to prevent overuse of CPU. */ + getitimer( ITIMER_REAL, &pahsc->pahsc_EntryTime ); +} + +static long SubtractTime_AminusB( struct itimerval *timeA, struct itimerval *timeB ) +{ + long secs = timeA->it_value.tv_sec - timeB->it_value.tv_sec; + long usecs = secs * 1000000; + usecs += (timeA->it_value.tv_usec - timeB->it_value.tv_usec); + return usecs; +} + +static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +{ + struct itimerval currentTime; + long insideCount; + long totalCount; /* Measure CPU utilization during this callback. */ + +#define LOWPASS_COEFFICIENT_0 (0.95) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if (pahsc == NULL) + return; + if (getitimer( ITIMER_REAL, ¤tTime ) == 0 ) + { + if (past->past_IfLastExitValid) + { + insideCount = SubtractTime_AminusB( &pahsc->pahsc_EntryTime, ¤tTime ); + pahsc->pahsc_InsideCountSum += insideCount; + totalCount = SubtractTime_AminusB( &pahsc->pahsc_LastExitTime, ¤tTime ); + pahsc->pahsc_TotalCountSum += totalCount; + /* DBUG(("insideCount = %d, totalCount = %d\n", insideCount, totalCount )); */ + /* Low pass filter the result because sometimes we get called several times in a row. */ + /* That can cause the TotalCount to be very low which can cause the usage to appear */ + /* unnaturally high. So we must filter numerator and denominator separately!!! */ + if (pahsc->pahsc_InsideCountSum > 0) + { + past->past_AverageInsideCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) + + (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_InsideCountSum)); + past->past_AverageTotalCount = ((LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) + + (LOWPASS_COEFFICIENT_1 * pahsc->pahsc_TotalCountSum)); + past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount; + pahsc->pahsc_InsideCountSum = 0; + pahsc->pahsc_TotalCountSum = 0; + } + } + past->past_IfLastExitValid = 1; + } + pahsc->pahsc_LastExitTime.it_value.tv_sec = 100; + pahsc->pahsc_LastExitTime.it_value.tv_usec = 0; + setitimer( ITIMER_REAL, &pahsc->pahsc_LastExitTime, NULL ); + past->past_IfLastExitValid = 1; +} /*----------- END OF CPU UTILIZATION CODE (from pa_linux_oss/pa_linux_oss.c v15)--------------------*/ + + +/*--------------------------------------------------------------------------------------*/ +PaError translateSGIerror(void) /* Calls oserror(), may be used after an SGI AL-library */ +{ /* call to report via ERR_RPT(), yields a PaError-num. */ + const char* a = "SGI AL "; /* (Not absolutely sure errno came from THIS thread! */ + switch(oserror()) /* Read IRIX man-pages about the _SGI_MP_SOURCE macro.) */ + { + case AL_BAD_OUT_OF_MEM: + ERR_RPT(("%sout of memory.\n", a)); + return paInsufficientMemory; /* Known PaError. */ + case AL_BAD_CONFIG: + ERR_RPT(("%sconfiguration invalid or NULL.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_CHANNELS: + ERR_RPT(("%schannels not 1,2 or 4.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_NO_PORTS: + ERR_RPT(("%sout of audio ports.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_DEVICE: + ERR_RPT(("%swrong device number.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_DEVICE_ACCESS: + ERR_RPT(("%swrong device access.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_DIRECTION: + ERR_RPT(("%sinvalid direction.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_SAMPFMT: + ERR_RPT(("%sdoesn't accept sampleformat.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_FLOATMAX: + ERR_RPT(("%smax float value is zero.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_WIDTH: + ERR_RPT(("%sunsupported samplewidth.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_QSIZE: + ERR_RPT(("%sinvalid queue size.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_PVBUFFER: + ERR_RPT(("%sPVbuffer null.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_BUFFERLENGTH_NEG: + ERR_RPT(("%snegative bufferlength.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_BUFFERLENGTH_ODD: + ERR_RPT(("%sodd bufferlength.\n", a)); + return paHostError; /* Generic PaError. */ + case AL_BAD_PARAM: + ERR_RPT(("%sparameter not valid for device.\n", a)); + return paHostError; /* Generic PaError. */ + default: + ERR_RPT(("%sunknown error.\n", a)); + return paHostError; /* Generic PaError. */ + } +} + +/*------------------------------------------------------------------------------------------*/ +/* Tries to set various rates and formats and fill in the device info structure. */ +static PaError Pa_sgiQueryDevice(long ALdev, /* (AL_DEFAULT_DEVICE) */ + PaDeviceID id, /* (DefaultI|ODeviceID()) */ + char* name, /* (for example "SGI AL") */ + internalPortAudioDevice* pad) /* Result written to pad. */ +{ + int format; + long min, max; /* To catch hardware characteristics. */ + ALseterrorhandler(0); /* 0 = turn off the default error handler. */ + /*--------------------------------------------------------------------------------------*/ + pad->pad_ALdevice = ALdev; /* Set the AL device number. */ + pad->pad_DeviceID = id; /* Set the PA device number. */ + if (strlen(name) > MAX_CHARS_DEVNAME) /* MAX_CHARS defined above. */ + { + ERR_RPT(("Pa_QueryDevice(): name too long (%s).\n", name)); + return paHostError; + } + strcpy(pad->pad_DeviceName, name); /* Write name-string. */ + pad->pad_Info.name = pad->pad_DeviceName; /* Set pointer,..hmmm. */ + /*--------------------------------- natively supported sample formats: -----------------*/ + pad->pad_Info.nativeSampleFormats = paInt16; /* Later also include paFloat32 | ..| etc. */ + /* Then also choose other CallConvertXX()! */ + /*--------------------------------- number of available i/o channels: ------------------*/ + if (ALgetminmax(ALdev, AL_INPUT_COUNT, &min, &max)) + return translateSGIerror(); + pad->pad_Info.maxInputChannels = max; + DBUG(("Pa_QueryDevice: maxInputChannels = %d\n", pad->pad_Info.maxInputChannels)) + if (ALgetminmax(ALdev, AL_OUTPUT_COUNT, &min, &max)) + return translateSGIerror(); + pad->pad_Info.maxOutputChannels = max; + DBUG(("Pa_QueryDevice: maxOutputChannels = %d\n", pad->pad_Info.maxOutputChannels)) + /*--------------------------------- supported samplerates: ----------------------*/ + pad->pad_Info.numSampleRates = 7; + pad->pad_Info.sampleRates = pad->pad_SampleRates; + pad->pad_SampleRates[0] = (double)AL_RATE_8000; /* long -> double. */ + pad->pad_SampleRates[1] = (double)AL_RATE_11025; + pad->pad_SampleRates[2] = (double)AL_RATE_16000; + pad->pad_SampleRates[3] = (double)AL_RATE_22050; + pad->pad_SampleRates[4] = (double)AL_RATE_32000; + pad->pad_SampleRates[5] = (double)AL_RATE_44100; + pad->pad_SampleRates[6] = (double)AL_RATE_48000; + if (ALgetminmax(ALdev, AL_INPUT_RATE, &min, &max)) /* Ask INPUT rate-max. */ + return translateSGIerror(); /* double -> long. */ + if (max != (long)(0.5 + pad->pad_SampleRates[6])) /* FP-compare not recommndd. */ + goto weird; + if (ALgetminmax(ALdev, AL_OUTPUT_RATE, &min, &max)) /* Ask OUTPUT rate-max. */ + return translateSGIerror(); + if (max != (long)(0.5 + pad->pad_SampleRates[6])) + { +weird: ERR_RPT(("Pa_sgiQueryDevice() did not confirm max samplerate (%ld)\n",max)); + return paHostError; /* Or make it a warning and just carry on... */ + } + /*-------------------------------------------------------------------------------*/ + return paNoError; +} + + +/*--------------------------------------------------------------------------------*/ +int Pa_CountDevices() /* Name of this function suggests it only counts and */ +{ /* is NOT destructive, it however resets whole PA ! */ + int numDevices = 0; /* Let 's not do that here. */ + internalPortAudioDevice* currentDevice = sDeviceList; /* COPY GLOBAL VAR. */ +#if 0 /* Remains from linux_oss v15: Pa_Initialize(), on */ + if (!currentDevice) /* its turn, calls PaHost_Init() via file pa_lib.c. */ + Pa_Initialize(); /* Isn't that a bit too 'rude'? Don't be too */ +#endif /* friendly to clients that forgot to initialize PA. */ + while (currentDevice) /* Slower but more elegant than the sNumDevices-way: */ + { + numDevices++; + currentDevice = currentDevice->pad_Next; + } + return numDevices; +} + +/*-------------------------------------------------------------------------------*/ +static internalPortAudioDevice *Pa_GetInternalDevice(PaDeviceID id) +{ + int numDevices = 0; + internalPortAudioDevice *res = (internalPortAudioDevice*)NULL; + internalPortAudioDevice *pad = sDeviceList; /* COPY GLOBAL VAR. */ + while (pad) /* pad may be NULL, that's ok, return 0. */ + { /* (Added ->pad_DeviceID field to the pad-struct, Pieter, 2001.) */ + if (pad->pad_DeviceID == id) /* This the device we were looking for? */ + res = pad; /* But keep on(!) counting so we don't */ + numDevices++; /* have to call Pa_CountDevices() later. */ + pad = pad->pad_Next; /* Advance to the next device or NULL. */ + } /* No assumptions about order of ID's in */ + if (!res) /* the list. */ + ERR_RPT(("Pa_GetInternalDevice() could not find specified ID (%d).\n",id)); + if ((id < 0) || (id >= numDevices)) + { + ERR_RPT(("Pa_GetInternalDevice() supplied with an illegal ID (%d).\n",id)); +#if 1 /* Be strict, even when found, */ + res = (internalPortAudioDevice*)NULL; /* do not accept illegal ID's. */ +#endif + } + return res; +} + +/*----------------------------------------------------------------------*/ +const PaDeviceInfo* Pa_GetDeviceInfo(PaDeviceID id) +{ + PaDeviceInfo* res = (PaDeviceInfo*)NULL; + internalPortAudioDevice* pad = Pa_GetInternalDevice(id); /* Call. */ + if (pad) + res = &pad->pad_Info; /* Not finding the specified ID is not */ + if (!res) /* the same as &pad->pad_Info == NULL. */ + ERR_RPT(("Pa_GetDeviceInfo() could not find it (ID=%d).\n", id)); + return res; /* So (maybe) a second/third ERR_RPT(). */ +} + +/*------------------------------------------------*/ +PaDeviceID Pa_GetDefaultInputDeviceID(void) +{ + return 0; /* 0 is the default device ID. */ +} +/*------------------------------------------------*/ +PaDeviceID Pa_GetDefaultOutputDeviceID(void) +{ + return 0; +} + +/*-------------------------------------------------------------------------------------------------*/ +/* Build linked a list with all the available audio devices on this SGI machine (only 1 for now). */ +PaError PaHost_Init(void) /* Called by Pa_Initialize() from pa_lib.c. */ +{ + internalPortAudioDevice* pad; + PaError r = paNoError; + int audioLibFileID; /* To test for the presence of audio. */ + + if (sDeviceList) /* Allow re-init, only warn, no error. */ + { + ERR_RPT(("Warning: PaHost_Init() did not really re-init PA.\n")); + return r; + } + /*------------- ADD THE SGI DEFAULT DEVICE TO THE LIST: ---------------------------------------*/ + audioLibFileID = open("/dev/hdsp/hdsp0master", O_RDONLY); /* Try to open Indigo style audio */ + if (audioLibFileID < 0) /* IO port. On failure, machine */ + { /* has no audio ability. */ + ERR_RPT(("PaHost_Init(): This machine has no (Indigo-style) audio abilities.\n")); + return paHostError; + } + close(audioLibFileID); /* Allocate fast mem to hold device info. */ + pad = PaHost_AllocateFastMemory(sizeof(internalPortAudioDevice)); + if (pad == NULL) + return paInsufficientMemory; + memset(pad, 0, sizeof(internalPortAudioDevice)); /* "pad->pad_Next = NULL" is more elegant. */ + r = Pa_sgiQueryDevice(AL_DEFAULT_DEVICE, /* Set AL device num (AL_DEFAULT_DEVICE). */ + Pa_GetDefaultOutputDeviceID(),/* Set PA device num (or InputDeviceID()). */ + "AL default", /* A suitable name. */ + pad); /* Write args and queried info into pad. */ + if (r != paNoError) + { + ERR_RPT(("Pa_QueryDevice for '%s' returned: %d\n", pad->pad_DeviceName, r)); + PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); /* sDeviceList still NULL ! */ + } + else + sDeviceList = pad; /* First element in linked list. pad->pad_Next already NULL. */ + /*------------- QUERY AND ADD MORE POSSIBLE SGI DEVICES TO THE LINKED LIST: -------------------*/ + /*---------------------------------------------------------------------------------------------*/ + return r; +} + +/*---------------------------------------------------------------------------------------------------*/ +static PaError Pa_SgiAudioProcess(internalPortAudioStream *past) /* Spawned by PaHost_StartEngine(). */ +{ + PaError result = paNoError; + PaHostSoundControl *pahsc; + + if (!past) + return paBadStreamPtr; + pahsc = (PaHostSoundControl*)past->past_DeviceData; + if (!pahsc) + return paInternalError; + past->past_IsActive = 1; /* Wasn't this already done by the calling parent?! */ + DBUG(("entering thread.\n")); + + while (!past->past_StopSoon) /* OR-ing StopSoon and StopNow here gives problems! */ + { + /*---------------------------------------- INPUT: ------------------------------------*/ + if (pahsc->pahsc_NativeInputBuffer) /* Then pahsc_ALportIN should also be there! */ + { + while (ALgetfilled(pahsc->pahsc_ALportIN) < pahsc->pahsc_SamplesPerInputBuffer) + { + /* Trying sginap(1); and usleep(); here... things get blocked under IRIX6.2. */ + if (past->past_StopNow) /* Don't let ALreadsamps() block */ + goto done; + } + if (ALreadsamps(pahsc->pahsc_ALportIN, (void*)pahsc->pahsc_NativeInputBuffer, + pahsc->pahsc_SamplesPerInputBuffer)) /* Number of samples instead */ + { /* of number of frames. */ + ERR_RPT(("ALreadsamps() failed.\n")); + result = paInternalError; + goto done; + } + } + /*---------------------------------------------------- USER CALLBACK ROUTINE: ----------*/ + /* DBUG(("Calling Pa_CallConvertInt16()...\n")); */ + Pa_StartUsageCalculation(past); /* Convert 16 bit native data to */ + result = Pa_CallConvertInt16(past, /* user data and call user routine. */ + pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_NativeOutputBuffer); + Pa_EndUsageCalculation(past); + if (result) + { + DBUG(("Pa_CallConvertInt16() returned %d, stopping...\n", result)); + goto done; /* This is apparently NOT an error! */ + } /* Just letting the userCallBack stop us. */ + /*---------------------------------------- OUTPUT: ------------------------------------*/ + if (pahsc->pahsc_NativeOutputBuffer) /* Then pahsc_ALportOUT should also be there! */ + { + while (ALgetfillable(pahsc->pahsc_ALportOUT) < pahsc->pahsc_SamplesPerOutputBuffer) + { + /* Trying sginap(1); and usleep(); here... things get blocked under IRIX6.2. */ + if (past->past_StopNow) /* Don't let ALwritesamps() block */ + goto done; + } + if (ALwritesamps(pahsc->pahsc_ALportOUT, (void*)pahsc->pahsc_NativeOutputBuffer, + pahsc->pahsc_SamplesPerOutputBuffer)) + { + ERR_RPT(("ALwritesamps() failed.\n")); + result = paInternalError; + goto done; + } + } + /*-------------------------------------------------------------------------------------*/ + } +done: + /* pahsc->pahsc_ThreadPID = -1; Hu? doesn't help!! (added by Pieter) */ + past->past_IsActive = 0; + DBUG(("leaving thread.\n")); + return result; +} + + +/*--------------------------------------------------------------------------------------*/ +PaError PaHost_OpenStream(internalPortAudioStream *past) +{ + PaError result = paNoError; + PaHostSoundControl *pahsc; + unsigned int minNumBuffers; + internalPortAudioDevice *padIN, *padOUT; /* For looking up native AL-numbers. */ + ALconfig sgiALconfig = NULL; /* IRIX-datatype. */ + long pvbuf[8]; /* To get/set hardware configs. */ + long sr, ALqsize; + DBUG(("PaHost_OpenStream() called.\n")); /* Alloc FASTMEM and init host data. */ + if (!past) + { + ERR_RPT(("Streampointer NULL!\n")); + result = paBadStreamPtr; goto done; + } + pahsc = (PaHostSoundControl*)PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); + if (pahsc == NULL) + { + ERR_RPT(("FAST Memory allocation failed.\n")); /* Pass trough some ERR_RPT-exit- */ + result = paInsufficientMemory; goto done; /* code (nothing will be freed). */ + } + memset(pahsc, 0, sizeof(PaHostSoundControl)); +/* pahsc->pahsc_threadPID = -1; Should pahsc_threadPID be inited to */ + past->past_DeviceData = (void*)pahsc; /* -1 instead of 0 ?? */ + /*--------------------------------------------------- Allocate native buffers: --------*/ + pahsc->pahsc_SamplesPerInputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */ + past->past_NumInputChannels; /* audio-thread. */ + pahsc->pahsc_BytesPerInputBuffer = pahsc->pahsc_SamplesPerInputBuffer * sizeof(short); + if (past->past_NumInputChannels > 0) /* Assumes short = 16 bits! */ + { + pahsc->pahsc_NativeInputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerInputBuffer); + if( pahsc->pahsc_NativeInputBuffer == NULL ) + { + ERR_RPT(("Fast memory allocation for input-buffer failed.\n")); + result = paInsufficientMemory; goto done; + } + } + pahsc->pahsc_SamplesPerOutputBuffer = past->past_FramesPerUserBuffer * /* Needed by the */ + past->past_NumOutputChannels; /* audio-thread. */ + pahsc->pahsc_BytesPerOutputBuffer = pahsc->pahsc_SamplesPerOutputBuffer * sizeof(short); + if (past->past_NumOutputChannels > 0) /* Assumes short = 16 bits! */ + { + pahsc->pahsc_NativeOutputBuffer = (short*)PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerOutputBuffer); + if (pahsc->pahsc_NativeOutputBuffer == NULL) + { + ERR_RPT(("Fast memory allocation for output-buffer failed.\n")); + result = paInsufficientMemory; goto done; + } + } + /*------------------------------------------ Manipulate hardware if necessary and allowed: --*/ + ALseterrorhandler(0); /* 0 = turn off the default error handler. */ + pvbuf[0] = AL_INPUT_RATE; + pvbuf[2] = AL_INPUT_COUNT; + pvbuf[4] = AL_OUTPUT_RATE; /* TO FIX: rates may be logically, not always in Hz! */ + pvbuf[6] = AL_OUTPUT_COUNT; + sr = (long)(past->past_SampleRate + 0.5); /* Common for input and output :-) */ + if (past->past_NumInputChannels > 0) /* We need to lookup the corre- */ + { /* sponding native AL-number(s). */ + padIN = Pa_GetInternalDevice(past->past_InputDeviceID); + if (!padIN) + { + ERR_RPT(("Pa_GetInternalDevice() for input failed.\n")); + result = paHostError; goto done; + } + if (ALgetparams(padIN->pad_ALdevice, &pvbuf[0], 4)) /* Although input and output will both be on */ + goto sgiError; /* the same AL-device, the AL-library might */ + if (pvbuf[1] != sr) /* contain more than AL_DEFAULT_DEVICE in */ + { /* Rate different from current harware-rate? the future. Therefore 2 seperate queries. */ + if (pvbuf[3] > 0) /* Means, there's other clients using AL-input-ports */ + { + ERR_RPT(("Sorry, not allowed to switch input-hardware to %ld Hz because \ +another process is currently using input at %ld kHz.\n", sr, pvbuf[1])); + result = paHostError; goto done; + } + pvbuf[1] = sr; /* Then set input-rate. */ + if (ALsetparams(padIN->pad_ALdevice, &pvbuf[0], 2)) + goto sgiError; /* WHETHER THIS SAMPLERATE WAS REALLY PRESENT IN OUR ARRAY OF RATES, */ + } /* IS NOT CHECKED, AT LEAST NOT BY ME, WITHIN THIS FILE! Does PA do? */ + } + if (past->past_NumOutputChannels > 0) /* CARE: padOUT/IN may NOT be NULL if Channels <= 0! */ + { /* We use padOUT/IN later on, or at least 1 of both. */ + padOUT = Pa_GetInternalDevice(past->past_OutputDeviceID); + if (!padOUT) + { + ERR_RPT(("Pa_GetInternalDevice() for output failed.\n")); + result = paHostError; goto done; + } + if (ALgetparams(padOUT->pad_ALdevice,&pvbuf[4], 4)) + goto sgiError; + if ((past->past_NumOutputChannels > 0) && (pvbuf[5] != sr)) + { /* Output needed and rate different from current harware-rate. */ + if (pvbuf[7] > 0) /* Means, there's other clients using AL-output-ports */ + { + ERR_RPT(("Sorry, not allowed to switch output-hardware to %ld Hz because \ +another process is currently using output at %ld kHz.\n", sr, pvbuf[5])); + result = paHostError; goto done; /* Will free again the inputbuffer */ + } /* that was just created above. */ + pvbuf[5] = sr; /* Then set output-rate. */ + if (ALsetparams(padOUT->pad_ALdevice, &pvbuf[4], 2)) + goto sgiError; + } + } + /*------------------------------------------ Construct an audio-port-configuration ----------*/ + sgiALconfig = ALnewconfig(); /* Change the SGI-AL-default-settings. */ + if (sgiALconfig == (ALconfig)0) /* sgiALconfig is released here after use! */ + goto sgiError; /* See that sgiALconfig is NOT released! */ + if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_TWOSCOMP)) /* Choose paInt16 as native i/o-format. */ + goto sgiError; + if (ALsetwidth (sgiALconfig, AL_SAMPLE_16)) /* Only meaningful when sample format for */ + goto sgiError; /* config is set to two's complement format. */ + /************************ Future versions might (dynamically) switch to 32-bit floats? ******* + if (ALsetsampfmt(sgiALconfig, AL_SAMPFMT_FLOAT)) (Then also call another CallConvert-func.) + goto sgiError; + if (ALsetfloatmax (sgiALconfig, 1.0)) Only meaningful when sample format for config + goto sgiError; is set to AL_SAMPFMT_FLOAT or AL_SAMPFMT_DOUBLE. */ + /*---------- ?? --------------------*/ + /* DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); */ + minNumBuffers = Pa_GetMinNumBuffers(past->past_FramesPerUserBuffer, past->past_SampleRate); + past->past_NumUserBuffers = (minNumBuffers > past->past_NumUserBuffers) ? + minNumBuffers : past->past_NumUserBuffers; + /*------------------------------------------------ Set internal AL queuesize (in samples) ----*/ + if (pahsc->pahsc_SamplesPerInputBuffer >= pahsc->pahsc_SamplesPerOutputBuffer) + ALqsize = (long)pahsc->pahsc_SamplesPerInputBuffer; + else /* Take the largest of the two amounts. */ + ALqsize = (long)pahsc->pahsc_SamplesPerOutputBuffer; + ALqsize *= 4; /* 4 times as large as amount per transfer! */ + if (ALsetqueuesize(sgiALconfig, ALqsize)) /* Or should we use past_NumUserBuffers here? */ + goto sgiError; /* Using 2 times may give glitches... */ + /* Have to work on ALsetqueuesize() above. */ + + /* Do ALsetchannels() later, apart per input and/or output. */ + /*----------------------------------------------- OPEN 1 OR 2 AL-DEVICES: --------------------*/ + if (past->past_OutputDeviceID == past->past_InputDeviceID) /* Who SETS these devive-numbers? */ + { + if ((past->past_NumOutputChannels > 0) && (past->past_NumInputChannels > 0)) + { + DBUG(("PaHost_OpenStream: opening both input and output channels.\n")); + /*------------------------- open output port: ----------------------------------*/ + if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels))) + goto sgiError; /* Returns 0 on success, -1 on failure. */ + pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig); + if (pahsc->pahsc_ALportOUT == (ALport)0) + goto sgiError; + /*------------------------- open input port: -----------------------------------*/ + if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels))) + goto sgiError; /* Returns 0 on success, -1 on failure. */ + pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig); + if (pahsc->pahsc_ALportIN == (ALport)0) + goto sgiError; /* For some reason the "patest_wire.c"-test crashes! */ + } /* Probably due to too small buffersizes?.... */ + else + { + ERR_RPT(("Cannot setup bidirectional stream between different devices.\n")); + result = paHostError; + goto done; + } + } + else /* (OutputDeviceID != InputDeviceID) */ + { + if (past->past_NumOutputChannels > 0) /* WRITE-ONLY: */ + { + /*------------------------- open output port: ----------------------------------*/ + DBUG(("PaHost_OpenStream: opening %d output channel(s) on %s.\n", + past->past_NumOutputChannels, padOUT->pad_DeviceName)); + if (ALsetchannels (sgiALconfig, (long)(past->past_NumOutputChannels))) + goto sgiError; /* Returns 0 on success, -1 on failure. */ + pahsc->pahsc_ALportOUT = ALopenport("PA sgi out", "w", sgiALconfig); + if (pahsc->pahsc_ALportOUT == (ALport)0) + goto sgiError; + } + if (past->past_NumInputChannels > 0) /* READ-ONLY: */ + { + /*------------------------- open input port: -----------------------------------*/ + DBUG(("PaHost_OpenStream: opening %d input channel(s) on %s.\n", + past->past_NumInputChannels, padIN->pad_DeviceName)); + if (ALsetchannels (sgiALconfig, (long)(past->past_NumInputChannels))) + goto sgiError; /* Returns 0 on success, -1 on failure. */ + pahsc->pahsc_ALportIN = ALopenport("PA sgi in", "r", sgiALconfig); + if (pahsc->pahsc_ALportIN == (ALport)0) + goto sgiError; + } + } + DBUG(("PaHost_OpenStream() succeeded.\n")); + goto done; /* (no errors occured) */ +sgiError: + result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */ +done: + if (sgiALconfig) + ALfreeconfig(sgiALconfig); /* We don't need that struct anymore. */ + if (result != paNoError) + PaHost_CloseStream(past); /* Frees memory (only if really allocated!). */ + return result; +} + +/*-----------------------------------------------------*/ +PaError PaHost_StartOutput(internalPortAudioStream *past) +{ + return paNoError; /* Hmm, not implemented yet? */ +} +PaError PaHost_StartInput(internalPortAudioStream *past) +{ + return paNoError; +} + +/*------------------------------------------------------------------------------*/ +PaError PaHost_StartEngine(internalPortAudioStream *past) +{ + PaHostSoundControl *pahsc; + int hres; + PaError result = paNoError; + + if (!past) /* Test argument. */ + { + ERR_RPT(("PaHost_StartEngine(NULL)!\n")); + return paBadStreamPtr; + } + pahsc = (PaHostSoundControl*)past->past_DeviceData; + if (!pahsc) + { + ERR_RPT(("PaHost_StartEngine(arg): arg->past_DeviceData == NULL!\n")); + return paHostError; + } + past->past_StopSoon = 0; /* Assume SGI ALport is already opened! */ + past->past_StopNow = 0; + past->past_IsActive = 1; + DBUG(("PaHost_StartEngine() called.\n")); + /* Use pthread_create() instead of __clone() because: */ + /* - pthread_create also works for other UNIX systems like Solaris, */ + /* - Java HotSpot VM crashes in pthread_setcanceltype() when using __clone(). */ + hres = pthread_create(&(pahsc->pahsc_ThreadPID), /* SPAWN AUDIO-CHILD. */ + NULL, /* pthread_attr_t * attr */ + (void*)Pa_SgiAudioProcess, + past); + if (hres) + { + result = paHostError; + sPaHostError = hres; + ERR_RPT(("PaHost_StartEngine() failed to spawn audio-thread.\n")); + } + return result; +} + +/*------------------------------------------------------------------------------*/ +PaError PaHost_StopEngine(internalPortAudioStream *past, int abort) +{ + int hres; + PaError result = paNoError; + PaHostSoundControl *pahsc; + + DBUG(("PaHost_StopEngine() called.\n")); + if (!past) + return paBadStreamPtr; + pahsc = (PaHostSoundControl*)past->past_DeviceData; + if (pahsc == NULL) + return result; /* paNoError (already stopped, no err?). */ + past->past_StopSoon = 1; /* Tell background thread to stop generating */ + if (abort) /* more and to let current data play out. If */ + past->past_StopNow = 1; /* aborting, tell backgrnd thread to stop NOW! */ + if (pahsc->pahsc_ThreadPID != -1) /* Join thread to recover memory resources. */ + { + DBUG(("pthread_join() called.\n")); + hres = pthread_join(pahsc->pahsc_ThreadPID, NULL); + if (hres) + { + result = paHostError; + sPaHostError = hres; + ERR_RPT(("PaHost_StopEngine() failed pthread_join().\n")); + } + pahsc->pahsc_ThreadPID = -1; + } + past->past_IsActive = 0; + return result; +} + +/*---------------------------------------------------------------*/ +PaError PaHost_StopOutput(internalPortAudioStream *past, int abort) +{ + return paNoError; /* Not implemented yet? */ +} +PaError PaHost_StopInput(internalPortAudioStream *past, int abort ) +{ + return paNoError; +} + +/*******************************************************************/ +PaError PaHost_CloseStream(internalPortAudioStream *past) +{ + PaHostSoundControl *pahsc; + PaError result = paNoError; + + DBUG(("PaHost_CloseStream() called.\n")); + if (past == NULL) + return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if (pahsc == NULL) /* If pahsc not NULL, past_DeviceData will be freed, and set to NULL. */ + return result; /* This test prevents from freeing NULL-pointers. */ + + if (pahsc->pahsc_ALportIN) + { + if (ALcloseport(pahsc->pahsc_ALportIN)) + result = translateSGIerror(); /* Translates SGI AL-code to PA-code and ERR_RPTs string. */ + else /* But go on anyway... to release other stuff... */ + pahsc->pahsc_ALportIN = (ALport)0; + } + if (pahsc->pahsc_ALportOUT) + { + if (ALcloseport(pahsc->pahsc_ALportOUT)) + result = translateSGIerror(); + else + pahsc->pahsc_ALportOUT = (ALport)0; + } + if (pahsc->pahsc_NativeInputBuffer) + { + PaHost_FreeFastMemory(pahsc->pahsc_NativeInputBuffer, pahsc->pahsc_BytesPerInputBuffer); + pahsc->pahsc_NativeInputBuffer = NULL; + } + if (pahsc->pahsc_NativeOutputBuffer) + { + PaHost_FreeFastMemory(pahsc->pahsc_NativeOutputBuffer, pahsc->pahsc_BytesPerOutputBuffer); + pahsc->pahsc_NativeOutputBuffer = NULL; + } + PaHost_FreeFastMemory(pahsc, sizeof(PaHostSoundControl)); + past->past_DeviceData = NULL; /* PaHost_OpenStream() allocated FAST. */ + return result; +} + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** set PA_MIN_LATENCY_MSEC=200 +** in the AUTOEXEC.BAT file and reboot. +** If the environment variable is not set, then the latency will be +** determined based on the OS. Windows NT has higher latency than Win95. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") + +int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ) +{ + return 2; +} +/* Hmmm, the note above isn't appropriate for SGI I'm afraid... */ +/* Do we HAVE to do it this way under IRIX???.... */ +/*--------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------*/ +PaError PaHost_Term(void) /* Frees all of the linked audio-devices. */ +{ /* Called by Pa_Terminate() from pa_lib.c. */ + internalPortAudioDevice *pad = sDeviceList, + *nxt; + while (pad) + { + DBUG(("PaHost_Term: freeing %s\n", pad->pad_DeviceName)); + nxt = pad->pad_Next; + PaHost_FreeFastMemory(pad, sizeof(internalPortAudioDevice)); + pad = nxt; /* PaHost_Init allocated this FAST MEM.*/ + } + sDeviceList = (internalPortAudioDevice*)NULL; + return 0; /* Got rid of sNumDevices=0; */ +} + +/***********************************************************************/ +void Pa_Sleep( long msec ) /* Sleep requested number of milliseconds. */ +{ +#if 0 + struct timeval timeout; + timeout.tv_sec = msec / 1000; + timeout.tv_usec = (msec % 1000) * 1000; + select(0, NULL, NULL, NULL, &timeout); +#else + long usecs = msec * 1000; + usleep( usecs ); +#endif +} + +/*---------------------------------------------------------------------------------------*/ +/* Allocate memory that can be accessed in real-time. This may need to be held in physi- */ +/* cal memory so that it is not paged to virtual memory. This call MUST be balanced with */ +/* a call to PaHost_FreeFastMemory(). */ +void *PaHost_AllocateFastMemory(long numBytes) +{ + void *addr = malloc(numBytes); + if (addr) + memset(addr, 0, numBytes); + return addr; +} + +/*---------------------------------------------------------------------------------------*/ +/* Free memory that could be accessed in real-time. This call MUST be balanced with a */ +/* call to PaHost_AllocateFastMemory(). */ +void PaHost_FreeFastMemory(void *addr, long numBytes) +{ + if (addr) + free(addr); +} + +/*----------------------------------------------------------*/ +PaError PaHost_StreamActive (internalPortAudioStream *past) +{ + PaHostSoundControl *pahsc; + if (past == NULL) + return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if (pahsc == NULL) + return paInternalError; + return (PaError)(past->past_IsActive != 0); +} + +/*-------------------------------------------------------------------*/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + internalPortAudioStream *past = (internalPortAudioStream *) stream; +/* FIXME - return actual frames played, not frames generated. +** Need to query the output device somehow. +*/ + return past->past_FrameCount; +} diff --git a/pd/portaudio/pa_tests/debug_convert.c b/pd/portaudio/pa_tests/debug_convert.c new file mode 100644 index 00000000..8e9dc6d1 --- /dev/null +++ b/pd/portaudio/pa_tests/debug_convert.c @@ -0,0 +1,131 @@ +/* + * $Id: debug_convert.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * Convert tagged values. + * + * Author: Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +//#define OUTPUT_DEVICE (11) +#define NUM_SECONDS (8) +#define SLEEP_DUR (800) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) + +#define NUM_BUFFERS (0) + +typedef struct +{ + unsigned int framesToGo; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + short *out = (short*)outputBuffer; + int i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( data->framesToGo < framesPerBuffer ) finished = 1; + + for( i=0; i +#include +#include "portaudio.h" +#include "pa_host.h" + +/*******************************************************************/ +int main(void); +int main(void) +{ + long max,min; + int i; + + for( i=0; i<10000; i++ ) + { + long dither = PaConvert_TriangularDither(); + // printf("dither = 0x%08X\n", dither ); + if( dither < min ) min = dither; + else if( dither > max ) max = dither; + } + printf("min = 0x%08X = %d, max = 0x%08X = %d\n", min, min, max, max ); +} diff --git a/pd/portaudio/pa_tests/debug_dual.c b/pd/portaudio/pa_tests/debug_dual.c new file mode 100644 index 00000000..b90ef290 --- /dev/null +++ b/pd/portaudio/pa_tests/debug_dual.c @@ -0,0 +1,183 @@ +/* + * $Id: debug_dual.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * debug_dual.c + * Try to open TWO streams on separate cards. + * Play a sine sweep using the Portable Audio api for several seconds. + * Hacked test for debugging PA. + * + * Author: Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define DEV_ID_1 (13) +#define DEV_ID_2 (15) +#define NUM_SECONDS (8) +#define SLEEP_DUR (800) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#if 0 +#define MIN_LATENCY_MSEC (200) +#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#else +#define NUM_BUFFERS (0) +#endif +#define MIN_FREQ (100.0f) +#define MAX_FREQ (4000.0f) +#define FREQ_SCALAR (1.00002f) +#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (400) +typedef struct +{ + float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation + float phase_increment; + float left_phase; + float right_phase; +} +paTestData; +/* Convert phase between and 1.0 to sine value + * using linear interpolation. + */ +float LookupSine( paTestData *data, float phase ); +float LookupSine( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->sine[index]; + float hi = data->sine[index+1]; + float val = lo + fract*(hi-lo); + return val; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + + for( i=0; ileft_phase); /* left */ + *out++ = LookupSine(data, data->right_phase); /* right */ + data->left_phase += data->phase_increment; + if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; + data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ + if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; + /* sweep frequency then start over. */ + data->phase_increment *= FREQ_SCALAR; + if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ); + } + return 0; +} + +PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, + paTestData *data ); +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream1, *stream2; + PaError err; + paTestData DATA1, DATA2; + printf("PortAudio Test: DUAL sine sweep. ask for %d buffers\n", NUM_BUFFERS ); + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = TestStart( &stream1, DEV_ID_1, &DATA1 ); + if( err != paNoError ) goto error; + err = TestStart( &stream2, DEV_ID_2, &DATA2 ); + if( err != paNoError ) goto error; + printf("Hit ENTER\n"); + getchar(); + err = Pa_StopStream( stream1 ); + if( err != paNoError ) goto error; + err = Pa_StopStream( stream2 ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} +PaError TestStart( PortAudioStream **streamPtr, PaDeviceID devID, paTestData *data ) +{ + PortAudioStream *stream; + PaError err; + int i; + /* initialise sinusoidal wavetable */ + for( i=0; isine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. ); + } + data->sine[TABLE_SIZE] = data->sine[0]; // set guard point + data->left_phase = data->right_phase = 0.0; + data->phase_increment = CalcPhaseIncrement(MIN_FREQ); + printf("PortAudio Test: output device = %d\n", devID ); + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + devID, + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + NUM_BUFFERS, /* number of buffers, if zero then use default minimum */ + paClipOff|paDitherOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + *streamPtr = stream; + return 0; +error: + return err; +} diff --git a/pd/portaudio/pa_tests/debug_multi_in.c b/pd/portaudio/pa_tests/debug_multi_in.c new file mode 100644 index 00000000..ef1d4a19 --- /dev/null +++ b/pd/portaudio/pa_tests/debug_multi_in.c @@ -0,0 +1,179 @@ +/* + * $Id: debug_multi_in.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * debug_multi_in.c + * Pass output from each of multiple channels + * to a stereo output using the Portable Audio api. + * Hacked test for debugging PA. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "portaudio.h" +//#define INPUT_DEVICE_NAME ("EWS88 MT Interleaved Rec") +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +//#define OUTPUT_DEVICE (18) +#define SAMPLE_RATE (22050) +#define FRAMES_PER_BUFFER (256) +#define MIN_LATENCY_MSEC (400) +#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +typedef struct +{ + int liveChannel; + int numChannels; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float *in = (float*)inputBuffer; + int i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( in == NULL ) return 0; + for( i=0; i<(int)framesPerBuffer; i++ ) + { + /* Copy one channel of input to output. */ + *out++ = in[data->liveChannel]; + *out++ = in[data->liveChannel]; + in += data->numChannels; + } + return 0; +} +/*******************************************************************/ +int PaFindDeviceByName( const char *name ) +{ + int i; + int numDevices; + const PaDeviceInfo *pdi; + int len = strlen( name ); + PaDeviceID result = paNoDevice; + numDevices = Pa_CountDevices(); + for( i=0; iname, len ) == 0 ) + { + result = i; + break; + } + } + return result; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + PaDeviceID inputDevice; + const PaDeviceInfo *pdi; + printf("PortAudio Test: input signal from each channel. %d buffers\n", NUM_BUFFERS ); + data.liveChannel = 0; + err = Pa_Initialize(); + if( err != paNoError ) goto error; +#ifdef INPUT_DEVICE_NAME + printf("Try to use device: %s\n", INPUT_DEVICE_NAME ); + inputDevice = PaFindDeviceByName(INPUT_DEVICE_NAME); + if( inputDevice == paNoDevice ) + { + printf("Could not find %s. Using default instead.\n", INPUT_DEVICE_NAME ); + inputDevice = Pa_GetDefaultInputDeviceID(); + } +#else + printf("Using default input device.\n"); + inputDevice = Pa_GetDefaultInputDeviceID(); +#endif + pdi = Pa_GetDeviceInfo( inputDevice ); + if( pdi == NULL ) + { + printf("Could not get device info!\n"); + goto error; + } + data.numChannels = pdi->maxInputChannels; + printf("Input Device name is %s\n", pdi->name ); + printf("Input Device has %d channels.\n", pdi->maxInputChannels); + err = Pa_OpenStream( + &stream, + inputDevice, + pdi->maxInputChannels, + paFloat32, /* 32 bit floating point input */ + NULL, + OUTPUT_DEVICE, + 2, + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + NUM_BUFFERS, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + data.liveChannel = 0; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + for( i=0; i +#include +#include "portaudio.h" + +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#define FREQ_INCR (300.0 / SAMPLE_RATE) +#define MAX_CHANNELS (64) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +typedef struct +{ + int numChannels; + double phases[MAX_CHANNELS]; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int frameIndex, channelIndex; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) + { + for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) + { + /* Output sine wave on every channel. */ + *out++ = (float) sin(data->phases[channelIndex]); + + /* Play each channel at a higher frequency. */ + data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); + if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); + } + } + + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + const PaDeviceInfo *pdi; + paTestData data = {0}; + printf("PortAudio Test: output sine wave on each channel.\n" ); + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE ); + data.numChannels = pdi->maxOutputChannels; + if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS; + printf("Number of Channels = %d\n", data.numChannels ); + + err = Pa_OpenStream( + &stream, + paNoDevice, /* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + OUTPUT_DEVICE, + data.numChannels, + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Hit ENTER to stop sound.\n"); + fflush(stdout); + getchar(); + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + Pa_CloseStream( stream ); + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/debug_record.c b/pd/portaudio/pa_tests/debug_record.c new file mode 100644 index 00000000..f82ea58c --- /dev/null +++ b/pd/portaudio/pa_tests/debug_record.c @@ -0,0 +1,339 @@ +/* + * $Id: debug_record.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * debug_record.c + * Record input into an array. + * Save array to a file. + * Based on patest_record.c but with various ugly debug hacks thrown in. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "portaudio.h" +#define SAMPLE_RATE (22050) +#define NUM_SECONDS (10) +#define SLEEP_DUR_MSEC (200) +#define FRAMES_PER_BUFFER (1<<10) +#define NUM_REC_BUFS (0) + +#if 1 +#define PA_SAMPLE_TYPE paFloat32 +typedef float SAMPLE; +#else +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#endif + +typedef struct +{ + long frameIndex; /* Index into sample array. */ + long maxFrameIndex; + long samplesPerFrame; + long numSamples; + SAMPLE *recordedSamples; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int recordCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = (SAMPLE*)inputBuffer; + SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame]; + long framesToCalc; + unsigned long i; + int finished; + unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; + + (void) outputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + framesToCalc = framesLeft; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + finished = 0; + } + if( inputBuffer == NULL ) + { + for( i=0; iframeIndex += framesToCalc; + return finished; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int playCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame]; + SAMPLE *wptr = (SAMPLE*)outputBuffer; + unsigned long i; + int finished; + unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; + if( outputBuffer == NULL ) return 0; + (void) inputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + /* final buffer... */ + for( i=0; iframeIndex += framesLeft; + finished = 1; + } + else + { + for( i=0; iframeIndex += framesPerBuffer; + finished = 0; + } + return finished; +} + +/****************************************************************/ +PaError TestRecording( paTestData *dataPtr ) +{ + PortAudioStream *stream; + PaError err; + int i; + + /* Record some audio. */ + err = Pa_OpenStream( + &stream, + Pa_GetDefaultInputDeviceID(), + dataPtr->samplesPerFrame, /* stereo input */ + PA_SAMPLE_TYPE, + NULL, + paNoDevice, + 0, + PA_SAMPLE_TYPE, + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + recordCallback, + dataPtr ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Now recording!\n"); fflush(stdout); + for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) + { + if( Pa_StreamActive( stream ) <= 0) + { + printf("Stream inactive!\n"); + break; + } + if( dataPtr->maxFrameIndex <= dataPtr->frameIndex ) + { + printf("Buffer recording complete.\n"); + break; + } + Pa_Sleep(100); + printf("index = %d\n", dataPtr->frameIndex ); fflush(stdout); + } + + printf("Finished loop. Close stream.\n"); fflush(stdout); + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + printf("Done.\n"); fflush(stdout); + { + SAMPLE max = 0; + SAMPLE posVal; + int i; + for( i=0; inumSamples; i++ ) + { + posVal = dataPtr->recordedSamples[i]; + if( posVal < 0 ) posVal = -posVal; + if( posVal > max ) max = posVal; + } + printf("Largest recorded sample = %d\n", max ); + } + /* Write recorded data to a file. */ +#if 0 + { + FILE *fid; + fid = fopen("recorded.raw", "wb"); + if( fid == NULL ) + { + printf("Could not open file."); + } + else + { + fwrite( dataPtr->recordedSamples, dataPtr->samplesPerFrame * sizeof(SAMPLE), totalFrames, fid ); + fclose( fid ); + printf("Wrote data to 'recorded.raw'\n"); + } + } +#endif + +error: + return err; +} + +/****************************************************************/ +PaError TestPlayback( paTestData *dataPtr ) +{ + PortAudioStream *stream; + PaError err; + int i; + + /* Playback recorded data. */ + dataPtr->frameIndex = 0; + printf("Begin playback.\n"); fflush(stdout); + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* NO input */ + PA_SAMPLE_TYPE, + NULL, + Pa_GetDefaultOutputDeviceID(), + dataPtr->samplesPerFrame, /* stereo output */ + PA_SAMPLE_TYPE, + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + playCallback, + dataPtr ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + printf("Waiting for playback to finish.\n"); fflush(stdout); + for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) + { + Pa_Sleep(100); + printf("index = %d\n", dataPtr->frameIndex ); + } + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + +error: + return err; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError err; + paTestData data; + long totalFrames; + long numBytes; + long i; + printf("patest_record.c\n"); fflush(stdout); + + data.frameIndex = 0; + data.samplesPerFrame = 2; + data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE; + + printf("totalFrames = %d\n", totalFrames ); fflush(stdout); + data.numSamples = totalFrames * data.samplesPerFrame; + + numBytes = data.numSamples * sizeof(SAMPLE); + data.recordedSamples = (SAMPLE *) malloc( numBytes ); + if( data.recordedSamples == NULL ) + { + printf("Could not allocate record array.\n"); + exit(1); + } + for( i=0; i +#include +#include +#include "portaudio.h" +#define SAMPLE_RATE (22050) +#define NUM_SECONDS (4) +#define SLEEP_DUR_MSEC (200) +#define FRAMES_PER_BUFFER (256) +#define NUM_REC_BUFS (0) + +#if 1 +#define PA_SAMPLE_TYPE paFloat32 +typedef float SAMPLE; +#else +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#endif + +typedef struct +{ + long frameIndex; /* Index into sample array. */ + long maxFrameIndex; + long samplesPerFrame; + long numSamples; + PortAudioStream *outputStream; + PortAudioStream *inputStream; + SAMPLE *recordedSamples; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int recordCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = (SAMPLE*)inputBuffer; + SAMPLE *wptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame]; + long framesToCalc; + unsigned long i; + int finished; + unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; + + (void) outputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + framesToCalc = framesLeft; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + finished = 0; + } + if( inputBuffer == NULL ) + { + for( i=0; iframeIndex += framesToCalc; + return finished; +} + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int playCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = &data->recordedSamples[data->frameIndex * data->samplesPerFrame]; + SAMPLE *wptr = (SAMPLE*)outputBuffer; + unsigned long i; + int finished; + unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; + if( outputBuffer == NULL ) return 0; + (void) inputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + /* final buffer... */ + for( i=0; iframeIndex += framesLeft; + finished = 1; + } + else + { + for( i=0; iframeIndex += framesPerBuffer; + finished = 0; + } + return finished; +} + +/****************************************************************/ +PaError TestRecording( paTestData *dataPtr ) +{ + PaError err; + int i; + int lastIndex = 0; + +/* Open input stream if not already open. */ + if( dataPtr->inputStream == NULL ) + { + /* Record some audio. */ + err = Pa_OpenStream( + &dataPtr->inputStream, + Pa_GetDefaultInputDeviceID(), + dataPtr->samplesPerFrame, /* stereo input */ + PA_SAMPLE_TYPE, + NULL, + paNoDevice, + 0, + PA_SAMPLE_TYPE, + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + recordCallback, + dataPtr ); + if( err != paNoError ) goto error; + } + + dataPtr->frameIndex = 0; + + err = Pa_StartStream( dataPtr->inputStream ); + if( err != paNoError ) goto error; + + printf("Now recording!\n"); fflush(stdout); + for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) + { + int frameIndex, delta; + Pa_Sleep(SLEEP_DUR_MSEC); + + frameIndex = dataPtr->frameIndex; + if( Pa_StreamActive( dataPtr->inputStream ) <= 0) + { + printf("Stream inactive!\n"); + break; + } + if( dataPtr->maxFrameIndex <= frameIndex ) + { + printf("Buffer recording complete.\n"); + break; + } + + delta = frameIndex - lastIndex; + lastIndex = frameIndex; + printf("index = %d, delta = %d\n", frameIndex, delta ); fflush(stdout); + } + + err = Pa_StopStream( dataPtr->inputStream ); + if( err != paNoError ) goto error; + + printf("Done.\n"); fflush(stdout); + +error: + return err; +} + +/****************************************************************/ +PaError TestPlayback( paTestData *dataPtr ) +{ + PaError err; + int i; + int lastIndex = 0; + + /* Playback recorded data. */ + dataPtr->frameIndex = 0; + printf("Begin playback.\n"); fflush(stdout); + +/* Open output stream if not already open. */ + if( dataPtr->outputStream == NULL ) + { + err = Pa_OpenStream( + &dataPtr->outputStream, + paNoDevice, + 0, /* NO input */ + PA_SAMPLE_TYPE, + NULL, + Pa_GetDefaultOutputDeviceID(), + dataPtr->samplesPerFrame, /* stereo output */ + PA_SAMPLE_TYPE, + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + playCallback, + dataPtr ); + if( err != paNoError ) goto error; + } + + err = Pa_StartStream( dataPtr->outputStream ); + if( err != paNoError ) goto error; + + printf("Waiting for playback to finish.\n"); fflush(stdout); + for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) + { + int frameIndex, delta; + Pa_Sleep(SLEEP_DUR_MSEC); + frameIndex = dataPtr->frameIndex; + delta = frameIndex - lastIndex; + lastIndex = frameIndex; + printf("index = %d, delta = %d\n", frameIndex, delta ); fflush(stdout); + } + + err = Pa_StopStream( dataPtr->outputStream ); + if( err != paNoError ) goto error; + +error: + return err; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError err; + paTestData data = { 0 }; + long totalFrames; + long numBytes; + long i; + printf("patest_record.c\n"); fflush(stdout); + +/* Set up test data structure and sample array. */ + data.frameIndex = 0; + data.samplesPerFrame = 2; + data.maxFrameIndex = totalFrames = NUM_SECONDS*SAMPLE_RATE; + + printf("totalFrames = %d\n", totalFrames ); fflush(stdout); + data.numSamples = totalFrames * data.samplesPerFrame; + + numBytes = data.numSamples * sizeof(SAMPLE); + data.recordedSamples = (SAMPLE *) malloc( numBytes ); + if( data.recordedSamples == NULL ) + { + printf("Could not allocate record array.\n"); + exit(1); + } + for( i=0; i + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +//#define OUTPUT_DEVICE (11) +#define NUM_SECONDS (8) +#define SLEEP_DUR (800) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#if 0 +#define MIN_LATENCY_MSEC (200) +#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#else +#define NUM_BUFFERS (0) +#endif +#define MIN_FREQ (100.0f) +#define MAX_FREQ (4000.0f) +#define FREQ_SCALAR (1.00002f) +#define CalcPhaseIncrement(freq) (freq/SAMPLE_RATE) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (400) +typedef struct +{ + float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation + float phase_increment; + float left_phase; + float right_phase; + unsigned int framesToGo; +} +paTestData; +/* Convert phase between and 1.0 to sine value + * using linear interpolation. + */ +float LookupSine( paTestData *data, float phase ); +float LookupSine( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->sine[index]; + float hi = data->sine[index+1]; + float val = lo + fract*(hi-lo); + return val; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int framesToCalc; + int i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( data->framesToGo < framesPerBuffer ) + { + framesToCalc = data->framesToGo; + data->framesToGo = 0; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + data->framesToGo -= framesPerBuffer; + } + + for( i=0; ileft_phase); /* left */ + *out++ = LookupSine(data, data->right_phase); /* right */ + data->left_phase += data->phase_increment; + if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; + data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ + if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; + /* sweep frequency then start over. */ + data->phase_increment *= FREQ_SCALAR; + if( data->phase_increment > CalcPhaseIncrement(MAX_FREQ) ) data->phase_increment = CalcPhaseIncrement(MIN_FREQ); + } + /* zero remainder of final buffer */ + for( ; i<(int)framesPerBuffer; i++ ) + { + *out++ = 0; /* left */ + *out++ = 0; /* right */ + } + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int totalSamps; + printf("PortAudio Test: output sine sweep. ask for %d buffers\n", NUM_BUFFERS ); + /* initialise sinusoidal wavetable */ + for( i=0; i +#include +#include "portaudio.h" + +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#define FREQ_INCR (300.0 / SAMPLE_RATE) +#define MAX_CHANNELS (64) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +typedef struct +{ + int numChannels; + double phases[MAX_CHANNELS]; + float amplitude; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int frameIndex, channelIndex; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) + { + for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) + { + /* Output sine wave on every channel. */ + *out++ = (float) ( data->amplitude * sin(data->phases[channelIndex]) ); + + /* Play each channel at a higher frequency. */ + data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); + if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); + } + } + + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + char pad[256]; + PortAudioStream *stream; + PaError err; + const PaDeviceInfo *pdi; + paTestData data = {0}; + printf("PortAudio Test: output sine wave on each channel.\n" ); + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE ); + data.numChannels = pdi->maxOutputChannels; + if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS; + printf("Number of Channels = %d\n", data.numChannels ); + data.amplitude = 1.0; + + err = Pa_OpenStream( + &stream, + paNoDevice, /* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + OUTPUT_DEVICE, + data.numChannels, + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + do + { + printf("Current amplitude = %f\n", data.amplitude ); + printf("Enter new amplitude or 'q' to quit.\n"); + fflush(stdout); + gets( pad ); + if( pad[0] != 'q' ) + { + // I tried to use atof but it seems to be broken on Mac OS X 10.1 + float amp; + sscanf( pad, "%f", & ); + data.amplitude = amp; + } + } while( pad[0] != 'q' ); + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + Pa_CloseStream( stream ); + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/debug_sine_formats.c b/pd/portaudio/pa_tests/debug_sine_formats.c new file mode 100644 index 00000000..3f75fa2e --- /dev/null +++ b/pd/portaudio/pa_tests/debug_sine_formats.c @@ -0,0 +1,202 @@ +/* + * $Id: debug_sine_formats.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_sine_formats.c + * Play a sine wave using the Portable Audio api for several seconds. + * Test various data formats. + * + * Author: Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define NUM_SECONDS (10) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) + +#define LEFT_FREQ (SAMPLE_RATE/512.0) /* So we hit 1.0 */ +#define RIGHT_FREQ (500.0) + +#define AMPLITUDE (1.0) + +/* Select ONE format for testing. */ +#define TEST_UINT8 (1) +#define TEST_INT8 (0) +#define TEST_INT16 (0) +#define TEST_FLOAT32 (0) + +#if TEST_UINT8 +#define TEST_FORMAT paUInt8 +typedef unsigned char SAMPLE_t; +#define SAMPLE_ZERO (0x80) +#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x))) +#define FORMAT_NAME "Unsigned 8 Bit" + +#elif TEST_INT8 +#define TEST_FORMAT paInt8 +typedef char SAMPLE_t; +#define SAMPLE_ZERO (0) +#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x))) +#define FORMAT_NAME "Signed 8 Bit" + +#elif TEST_INT16 +#define TEST_FORMAT paInt16 +typedef short SAMPLE_t; +#define SAMPLE_ZERO (0) +#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767 * (x))) +#define FORMAT_NAME "Signed 16 Bit" + +#elif TEST_FLOAT32 +#define TEST_FORMAT paFloat32 +typedef float SAMPLE_t; +#define SAMPLE_ZERO (0.0) +#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x)) +#define FORMAT_NAME "Float 32 Bit" +#endif + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + + +typedef struct +{ + double left_phase; + double right_phase; + unsigned int framesToGo; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE_t *out = (SAMPLE_t *)outputBuffer; + SAMPLE_t sample; + int i; + int framesToCalc; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( data->framesToGo < framesPerBuffer ) + { + framesToCalc = data->framesToGo; + data->framesToGo = 0; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + data->framesToGo -= framesPerBuffer; + } + + for( i=0; ileft_phase += (LEFT_FREQ / SAMPLE_RATE); + if( data->left_phase > 1.0) data->left_phase -= 1.0; + sample = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. ))); /**/ + *out++ = sample; +/* *out++ = sample; /**/ +/* *out++ = 0; /**/ + + data->right_phase += (RIGHT_FREQ / SAMPLE_RATE); + if( data->right_phase > 1.0) data->right_phase -= 1.0; + *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. ))); /**/ +/* *out++ = 0; /* */ + } + /* zero remainder of final buffer */ + for( ; i<(int)framesPerBuffer; i++ ) + { + *out++ = SAMPLE_ZERO; /* left */ + *out++ = SAMPLE_ZERO; /* right */ + } + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int totalSamps; + + printf("PortAudio Test: output " FORMAT_NAME "\n"); + + + data.left_phase = data.right_phase = 0.0; + data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + TEST_FORMAT, + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 2, /* stereo output */ + TEST_FORMAT, + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS ); + while( Pa_StreamActive( stream ) ) Pa_Sleep(10); + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + + printf("PortAudio Test Finished: " FORMAT_NAME "\n"); + + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/debug_srate.c b/pd/portaudio/pa_tests/debug_srate.c new file mode 100644 index 00000000..b3e3b70f --- /dev/null +++ b/pd/portaudio/pa_tests/debug_srate.c @@ -0,0 +1,265 @@ +/* + * $Id: debug_srate.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * debug_record_reuse.c + * Record input into an array. + * Save array to a file. + * Based on patest_record.c but with various ugly debug hacks thrown in. + * Loop twice and reuse same streams. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "portaudio.h" + +#define EWS88MT_12_REC (1) +#define EWS88MT_12_PLAY (10) +#define SBLIVE_REC (2) +#define SBLIVE_PLAY (11) + +#if 0 +#define INPUT_DEVICE_ID Pa_GetDefaultInputDeviceID() +#define OUTPUT_DEVICE_ID Pa_GetDefaultOutputDeviceID() +#else +#define INPUT_DEVICE_ID (EWS88MT_12_REC) +#define OUTPUT_DEVICE_ID (SBLIVE_PLAY) +#endif + +#define INPUT_SAMPLE_RATE (22050.0) +#define OUTPUT_SAMPLE_RATE (22050.0) +#define NUM_SECONDS (4) +#define SLEEP_DUR_MSEC (1000) +#define FRAMES_PER_BUFFER (64) +#define NUM_REC_BUFS (0) +#define SAMPLES_PER_FRAME (2) + +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; + +typedef struct +{ + long frameIndex; /* Index into sample array. */ +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int recordCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData *) userData; + (void) outputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( inputBuffer != NULL ) + { + data->frameIndex += framesPerBuffer; + } + return 0; +} + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int playCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData *) userData; + (void) inputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( outputBuffer != NULL ) + { + data->frameIndex += framesPerBuffer; + } + return 0; +} + +/****************************************************************/ +PaError MeasureStreamRate( PortAudioStream *stream, paTestData *dataPtr, double *ratePtr ) +{ + PaError err; + int i; + int totalFrames = 0; + int totalMSec = 0; + + dataPtr->frameIndex = 0; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + for( i=0; i<(NUM_SECONDS*1000/SLEEP_DUR_MSEC); i++ ) + { + int delta, endIndex; + + int startIndex = dataPtr->frameIndex; + Pa_Sleep(SLEEP_DUR_MSEC); + endIndex = dataPtr->frameIndex; + + delta = endIndex - startIndex; + totalFrames += delta; + totalMSec += SLEEP_DUR_MSEC; + + printf("index = %d, delta = %d\n", endIndex, delta ); fflush(stdout); + } + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + *ratePtr = (totalFrames * 1000.0) / totalMSec; + +error: + return err; +} + +void ReportRate( double measuredRate, double expectedRate ) +{ + double error; + + error = (measuredRate - expectedRate) / expectedRate; + error = (error < 0 ) ? -error : error; + + printf("Measured rate = %6.1f, expected rate = %6.1f\n", + measuredRate, expectedRate ); + if( error > 0.1 ) + { + printf("ERROR: unexpected rate! --------------------- ERROR!\n"); + } + else + { + printf("SUCCESS: rate within tolerance!\n"); + } +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError err; + paTestData data = { 0 }; + long i; + double rate; + const PaDeviceInfo *pdi; + + PortAudioStream *outputStream; + PortAudioStream *inputStream; + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + + pdi = Pa_GetDeviceInfo( INPUT_DEVICE_ID ); + printf("Input device = %s\n", pdi->name ); + pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE_ID ); + printf("Output device = %s\n", pdi->name ); + +/* Open input stream. */ + err = Pa_OpenStream( + &inputStream, + INPUT_DEVICE_ID, + SAMPLES_PER_FRAME, /* stereo input */ + PA_SAMPLE_TYPE, + NULL, + paNoDevice, + 0, + PA_SAMPLE_TYPE, + NULL, + INPUT_SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + NUM_REC_BUFS, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + recordCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_OpenStream( + &outputStream, + paNoDevice, + 0, /* NO input */ + PA_SAMPLE_TYPE, + NULL, + OUTPUT_DEVICE_ID, + SAMPLES_PER_FRAME, /* stereo output */ + PA_SAMPLE_TYPE, + NULL, + OUTPUT_SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + playCallback, + &data ); + if( err != paNoError ) goto error; + +/* Record and playback multiple times. */ + for( i=0; i<2; i++ ) + { + printf("Measuring INPUT ------------------------- \n"); + err = MeasureStreamRate( inputStream, &data, &rate ); + if( err != paNoError ) goto error; + ReportRate( rate, INPUT_SAMPLE_RATE ); + + printf("Measuring OUTPUT ------------------------- \n"); + err = MeasureStreamRate( outputStream, &data, &rate ); + if( err != paNoError ) goto error; + ReportRate( rate, OUTPUT_SAMPLE_RATE ); + } + +/* Clean up. */ + err = Pa_CloseStream( inputStream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( outputStream ); + if( err != paNoError ) goto error; + + if( err != paNoError ) goto error; + + Pa_Terminate(); + + printf("Test complete.\n"); fflush(stdout); + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + if( err == paHostError ) + { + fprintf( stderr, "Host Error number: %d\n", Pa_GetHostError() ); + } + return -1; +} diff --git a/pd/portaudio/pa_tests/debug_test1.c b/pd/portaudio/pa_tests/debug_test1.c new file mode 100644 index 00000000..05370b00 --- /dev/null +++ b/pd/portaudio/pa_tests/debug_test1.c @@ -0,0 +1,114 @@ +/* + * $Id: debug_test1.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + patest1.c + Ring modulate the audio input with a 441hz sine wave for 20 seconds + using the Portable Audio api + Author: Ross Bencina + Modifications: + April 5th, 2001 - PLB - Check for NULL inputBuffer. +*/ +#include +#include +#include "portaudio.h" +#ifndef M_PI +#define M_PI (3.14159265) +#endif +typedef struct +{ + float sine[100]; + int phase; + int sampsToGo; +} +patest1data; +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long bufferFrames, + PaTimestamp outTime, void *userData ) +{ + patest1data *data = (patest1data*)userData; + float *in = (float*)inputBuffer; + float *out = (float*)outputBuffer; + int framesToCalc = bufferFrames; + unsigned long i; + int finished = 0; + if(inputBuffer == NULL) return 0; + if( data->sampsToGo < bufferFrames ) + { + finished = 1; + } + for( i=0; iphase >= 100 ) + data->phase = 0; + } + data->sampsToGo -= bufferFrames; + /* zero remainder of final buffer if not already done */ + for( ; i +#include +#include "portaudio.h" +/*******************************************************************/ +static void PrintSupportedStandardSampleRates( + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters ) +{ + static double standardSampleRates[] = { + 8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, + 44100.0, 48000.0, 88200.0, 96000.0, -1 /* negative terminated list */ + }; + int i, printCount; + PaError err; + + printCount = 0; + for( i=0; standardSampleRates[i] > 0; i++ ) + { + err = Pa_IsFormatSupported( inputParameters, outputParameters, standardSampleRates[i] ); + if( err == paFormatIsSupported ) + { + if( printCount == 0 ) + { + printf( "\t%8.2f", standardSampleRates[i] ); + printCount = 1; + } + else if( printCount == 4 ) + { + printf( ",\n\t%8.2f", standardSampleRates[i] ); + printCount = 1; + } + else + { + printf( ", %8.2f", standardSampleRates[i] ); + ++printCount; + } + } + } + if( !printCount ) + printf( "None\n" ); + else + printf( "\n" ); +} +/*******************************************************************/ +int main(void); +int main(void) +{ + int i, numDevices, defaultDisplayed; + const PaDeviceInfo *deviceInfo; + PaStreamParameters inputParameters, outputParameters; + PaError err; + + + Pa_Initialize(); + + printf( "PortAudio version number = %d\nPortAudio version text = '%s'\n", + Pa_GetVersion(), Pa_GetVersionText() ); + + + numDevices = Pa_CountDevices(); + if( numDevices < 0 ) + { + printf( "ERROR: Pa_CountDevices returned 0x%x\n", numDevices ); + err = numDevices; + goto error; + } + + printf( "Number of devices = %d\n", numDevices ); + for( i=0; ihostApi )->defaultInputDevice ) + { + const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi ); + printf( "[ Default %s Input", hostInfo->name ); + defaultDisplayed = 1; + } + + if( i == Pa_GetDefaultOutputDevice() ) + { + printf( (defaultDisplayed ? "," : "[") ); + printf( " Default Output" ); + defaultDisplayed = 1; + } + else if( i == Pa_GetHostApiInfo( deviceInfo->hostApi )->defaultOutputDevice ) + { + const PaHostApiInfo *hostInfo = Pa_GetHostApiInfo( deviceInfo->hostApi ); + printf( (defaultDisplayed ? "," : "[") ); + printf( " Default %s Output", hostInfo->name ); + defaultDisplayed = 1; + } + + if( defaultDisplayed ) + printf( " ]\n" ); + + /* print device info fields */ + printf( "Name = %s\n", deviceInfo->name ); + printf( "Host API = %s\n", Pa_GetHostApiInfo( deviceInfo->hostApi )->name ); + printf( "Max inputs = %d", deviceInfo->maxInputChannels ); + printf( ", Max outputs = %d\n", deviceInfo->maxOutputChannels ); + + printf( "Default low input latency = %8.3f\n", deviceInfo->defaultLowInputLatency ); + printf( "Default low output latency = %8.3f\n", deviceInfo->defaultLowOutputLatency ); + printf( "Default high input latency = %8.3f\n", deviceInfo->defaultHighInputLatency ); + printf( "Default high output latency = %8.3f\n", deviceInfo->defaultHighOutputLatency ); + + printf( "Default sample rate = %8.2f\n", deviceInfo->defaultSampleRate ); + + /* poll for standard sample rates */ + inputParameters.device = i; + inputParameters.channelCount = deviceInfo->maxInputChannels; + inputParameters.sampleFormat = paInt16; + inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ + inputParameters.hostApiSpecificStreamInfo = NULL; + + outputParameters.device = i; + outputParameters.channelCount = deviceInfo->maxOutputChannels; + outputParameters.sampleFormat = paInt16; + outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */ + outputParameters.hostApiSpecificStreamInfo = NULL; + + if( inputParameters.channelCount > 0 ) + { + printf("Supported standard sample rates\n for half-duplex 16 bit %d channel input = \n", + inputParameters.channelCount ); + PrintSupportedStandardSampleRates( &inputParameters, NULL ); + } + + if( outputParameters.channelCount > 0 ) + { + printf("Supported standard sample rates\n for half-duplex 16 bit %d channel output = \n", + outputParameters.channelCount ); + PrintSupportedStandardSampleRates( NULL, &outputParameters ); + } + + if( inputParameters.channelCount > 0 && outputParameters.channelCount > 0 ) + { + printf("Supported standard sample rates\n for full-duplex 16 bit %d channel input, %d channel output = \n", + inputParameters.channelCount, outputParameters.channelCount ); + PrintSupportedStandardSampleRates( &inputParameters, &outputParameters ); + } + } + + Pa_Terminate(); + + printf("----------------------------------------------\n"); + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/pa_fuzz.c b/pd/portaudio/pa_tests/pa_fuzz.c new file mode 100644 index 00000000..1ec88785 --- /dev/null +++ b/pd/portaudio/pa_tests/pa_fuzz.c @@ -0,0 +1,168 @@ +/* + * $Id: pa_fuzz.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * pa_fuzz.c + * Distort input like a fuzz boz. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +/* +** Note that many of the older ISA sound cards on PCs do NOT support +** full duplex audio (simultaneous record and playback). +** And some only support full duplex at lower sample rates. +*/ +#define SAMPLE_RATE (44100) +#define PA_SAMPLE_TYPE paFloat32 +#define FRAMES_PER_BUFFER (64) + +typedef float SAMPLE; + +float CubicAmplifier( float input ); +static int fuzzCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ); + +/* Non-linear amplifier with soft distortion curve. */ +float CubicAmplifier( float input ) +{ + float output, temp; + if( input < 0.0 ) + { + temp = input + 1.0f; + output = (temp * temp * temp) - 1.0f; + } + else + { + temp = input - 1.0f; + output = (temp * temp * temp) + 1.0f; + } + + return output; +} +#define FUZZ(x) CubicAmplifier(CubicAmplifier(CubicAmplifier(CubicAmplifier(x)))) + +static int gNumNoInputs = 0; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int fuzzCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + SAMPLE *out = (SAMPLE*)outputBuffer; + SAMPLE *in = (SAMPLE*)inputBuffer; + unsigned int i; + (void) timeInfo; /* Prevent unused variable warnings. */ + (void) statusFlags; + (void) userData; + + if( inputBuffer == NULL ) + { + for( i=0; idefaultLowInputLatency; + inputParameters.hostApiSpecificStreamInfo = NULL; + + outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */ + outputParameters.channelCount = 2; /* stereo output */ + outputParameters.sampleFormat = PA_SAMPLE_TYPE; + outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream( + &stream, + &inputParameters, + &outputParameters, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + 0, // paClipOff, /* we won't output out of range samples so don't bother clipping them */ + fuzzCallback, + NULL ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Hit ENTER to stop program.\n"); + getchar(); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + printf("Finished. gNumNoInputs = %d\n", gNumNoInputs ); + Pa_Terminate(); + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return -1; +} diff --git a/pd/portaudio/pa_tests/pa_minlat.c b/pd/portaudio/pa_tests/pa_minlat.c new file mode 100644 index 00000000..736445a7 --- /dev/null +++ b/pd/portaudio/pa_tests/pa_minlat.c @@ -0,0 +1,176 @@ +/* + * $Id: pa_minlat.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * paminlat.c + * Experiment with different numbers of buffers to determine the + * minimum latency for a computer. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#include "portaudio.h" + +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TWOPI (M_PI * 2.0) + +#define DEFAULT_BUFFER_SIZE (32) + +typedef struct +{ + double left_phase; + double right_phase; +} +paTestData; + +/* Very simple synthesis routine to generate two sine waves. */ +static int paminlatCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + double left_phaseInc = 0.02; + double right_phaseInc = 0.06; + + double left_phase = data->left_phase; + double right_phase = data->right_phase; + + for( i=0; i TWOPI ) left_phase -= TWOPI; + *out++ = (float) sin( left_phase ); + + right_phase += right_phaseInc; + if( right_phase > TWOPI ) right_phase -= TWOPI; + *out++ = (float) sin( right_phase ); + } + + data->left_phase = left_phase; + data->right_phase = right_phase; + return 0; +} +void main( int argc, char **argv ); +void main( int argc, char **argv ) +{ + PaStream *stream; + PaError err; + paTestData data; + int go; + int outLatency = 0; + int minLatency = DEFAULT_BUFFER_SIZE * 2; + int framesPerBuffer; + double sampleRate = 44100.0; + char str[256]; + printf("pa_minlat - Determine minimum latency for your computer.\n"); + printf(" usage: pa_minlat {userBufferSize}\n"); + printf(" for example: pa_minlat 64\n"); + printf("Adjust your stereo until you hear a smooth tone in each speaker.\n"); + printf("Then try to find the smallest number of frames that still sounds smooth.\n"); + printf("Note that the sound will stop momentarily when you change the number of buffers.\n"); + + /* Get bufferSize from command line. */ + framesPerBuffer = ( argc > 1 ) ? atol( argv[1] ) : DEFAULT_BUFFER_SIZE; + printf("Frames per buffer = %d\n", framesPerBuffer ); + + data.left_phase = data.right_phase = 0.0; + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + outLatency = sampleRate * 200.0 / 1000.0; // 200 msec + + /* Try different numBuffers in a loop. */ + go = 1; + while( go ) + { + + printf("Latency = %d frames = %6.1f msec.\n", outLatency, + (outLatency * 1000.0 / sampleRate) ); + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + 0, + NULL, + Pa_GetDefaultOutputDevice(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + outLatency, + NULL, + sampleRate, + framesPerBuffer, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + paminlatCallback, + &data ); + if( err != paNoError ) goto error; + if( stream == NULL ) goto error; + + /* Start audio. */ + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + /* Ask user for a new nlatency. */ + printf("\nMove windows around to see if the sound glitches.\n"); + printf("Latency currently %d, enter new number, or 'q' to quit: ", outLatency ); + gets( str ); + if( str[0] == 'q' ) go = 0; + else + { + outLatency = atol( str ); + if( outLatency < minLatency ) + { + printf( "Latency below minimum of %d! Set to minimum!!!\n", minLatency ); + outLatency = minLatency; + } + } + /* Stop sound until ENTER hit. */ + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + } + printf("A good setting for latency would be somewhat higher than\n"); + printf("the minimum latency that worked.\n"); + printf("PortAudio: Test finished.\n"); + Pa_Terminate(); + return; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); +} diff --git a/pd/portaudio/pa_tests/paqa_devs.c b/pd/portaudio/pa_tests/paqa_devs.c new file mode 100644 index 00000000..904a6082 --- /dev/null +++ b/pd/portaudio/pa_tests/paqa_devs.c @@ -0,0 +1,317 @@ +/* + * $Id: paqa_devs.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * paqa_devs.c + * Self Testing Quality Assurance app for PortAudio + * Try to open each device and run through all the + * possible configurations. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#include "pa_trace.h" +/****************************************** Definitions ***********/ +#define MODE_INPUT (0) +#define MODE_OUTPUT (1) +typedef struct PaQaData +{ + unsigned long framesLeft; + int numChannels; + int bytesPerSample; + int mode; + short sawPhase; + PaSampleFormat format; +} +PaQaData; +/****************************************** Prototypes ***********/ +static void TestDevices( int mode ); +static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels ); +static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels, PaSampleFormat format ); +static int QaCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +/****************************************** Globals ***********/ +static int gNumPassed = 0; +static int gNumFailed = 0; +/****************************************** Macros ***********/ +/* Print ERROR if it fails. Tally success or failure. */ +/* Odd do-while wrapper seems to be needed for some compilers. */ +#define EXPECT(_exp) \ + do \ + { \ + if ((_exp)) {\ + /* printf("SUCCESS for %s\n", #_exp ); */ \ + gNumPassed++; \ + } \ + else { \ + printf("ERROR - 0x%x - %s for %s\n", result, \ + ((result == 0) ? "-" : Pa_GetErrorText(result)), \ + #_exp ); \ + gNumFailed++; \ + goto error; \ + } \ + } while(0) +/*******************************************************************/ +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int QaCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + unsigned long i; + short phase; + PaQaData *data = (PaQaData *) userData; + (void) inputBuffer; + (void) outTime; + + /* Play simle sawtooth wave. */ + if( data->mode == MODE_OUTPUT ) + { + phase = data->sawPhase; + switch( data->format ) + { + case paFloat32: + { + float *out = (float *) outputBuffer; + for( i=0; inumChannels == 2 ) + { + *out++ = (float) (phase * (1.0 / 32768.0)); + } + } + } + break; + + case paInt32: + { + int *out = (int *) outputBuffer; + for( i=0; inumChannels == 2 ) + { + *out++ = ((int) phase ) << 16; + } + } + } + break; + case paInt16: + { + short *out = (short *) outputBuffer; + for( i=0; inumChannels == 2 ) + { + *out++ = phase; + } + } + } + break; + + default: + { + unsigned char *out = (unsigned char *) outputBuffer; + unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; + for( i=0; isawPhase = phase; + } + /* Are we through yet? */ + if( data->framesLeft > framesPerBuffer ) + { + AddTraceMessage("QaCallback: running. framesLeft", data->framesLeft ); + data->framesLeft -= framesPerBuffer; + return 0; + } + else + { + AddTraceMessage("QaCallback: DONE! framesLeft", data->framesLeft ); + data->framesLeft = 0; + return 1; + } +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError result; + EXPECT( ((result=Pa_Initialize()) == 0) ); + printf("Test OUTPUT ---------------\n"); + TestDevices( MODE_OUTPUT ); + printf("Test INPUT ---------------\n"); + TestDevices( MODE_INPUT ); +error: + Pa_Terminate(); + printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed ); +} +/******************************************************************* +* Try each output device, through its full range of capabilities. */ +static void TestDevices( int mode ) +{ + int id,jc,kr; + int maxChannels; + const PaDeviceInfo *pdi; + int numDevices = Pa_CountDevices(); + /* Iterate through all devices. */ + for( id=0; idmaxInputChannels : pdi->maxOutputChannels; + for( jc=1; jc<=maxChannels; jc++ ) + { + printf("Name = %s\n", pdi->name ); + /* Try each legal sample rate. */ + if( pdi->numSampleRates == -1 ) + { + double low, high; + low = pdi->sampleRates[0]; + high = pdi->sampleRates[1]; + if( low < 8000.0 ) low = 8000.0; + TestFormats( mode, id, low, jc ); +#define TESTSR(sr) {if(((sr)>=low) && ((sr)<=high)) TestFormats( mode, id, (sr), jc ); } + + TESTSR(11025.0); + TESTSR(22050.0); + TESTSR(44100.0); + TestFormats( mode, id, high, jc ); + } + else + { + for( kr=0; krnumSampleRates; kr++ ) + { + TestFormats( mode, id, pdi->sampleRates[kr], jc ); + } + } + } + } +} +/*******************************************************************/ +static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels ) +{ + TestAdvance( mode, deviceID, sampleRate, numChannels, paFloat32 ); /* */ + TestAdvance( mode, deviceID, sampleRate, numChannels, paInt16 ); /* */ + TestAdvance( mode, deviceID, sampleRate, numChannels, paInt32 ); /* */ + /* TestAdvance( mode, deviceID, sampleRate, numChannels, paInt24 ); */ +} +/*******************************************************************/ +static int TestAdvance( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels, PaSampleFormat format ) +{ + PortAudioStream *stream = NULL; + PaError result; + PaQaData myData; +#define FRAMES_PER_BUFFER (64) + printf("------ TestAdvance: %s, device = %d, rate = %g, numChannels = %d, format = %d -------\n", + ( mode == MODE_INPUT ) ? "INPUT" : "OUTPUT", + deviceID, sampleRate, numChannels, format); + /* Setup data for synthesis thread. */ + myData.framesLeft = (unsigned long) (sampleRate * 100); /* 100 seconds */ + myData.numChannels = numChannels; + myData.mode = mode; + myData.format = format; + switch( format ) + { + case paFloat32: + case paInt32: + case paInt24: + myData.bytesPerSample = 4; + break; + case paPackedInt24: + myData.bytesPerSample = 3; + break; + default: + myData.bytesPerSample = 2; + break; + } + EXPECT( ((result = Pa_OpenStream( + &stream, + ( mode == MODE_INPUT ) ? deviceID : paNoDevice, + ( mode == MODE_INPUT ) ? numChannels : 0, + format, + NULL, + ( mode == MODE_OUTPUT ) ? deviceID : paNoDevice, + ( mode == MODE_OUTPUT ) ? numChannels : 0, + format, + NULL, + sampleRate, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + QaCallback, + &myData ) + ) == 0) ); + if( stream ) + { + PaTimestamp oldStamp, newStamp; + unsigned long oldFrames; + int minDelay = ( mode == MODE_INPUT ) ? 1000 : 400; + int minNumBuffers = Pa_GetMinNumBuffers( FRAMES_PER_BUFFER, sampleRate ); + int msec = (int) ((minNumBuffers * 3 * 1000.0 * FRAMES_PER_BUFFER) / sampleRate); + if( msec < minDelay ) msec = minDelay; + printf("msec = %d\n", msec); /**/ + EXPECT( ((result=Pa_StartStream( stream )) == 0) ); + /* Check to make sure PortAudio is advancing timeStamp. */ + result = paNoError; + oldStamp = Pa_StreamTime(stream); + Pa_Sleep(msec); + newStamp = Pa_StreamTime(stream); + printf("oldStamp = %g,newStamp = %g\n", oldStamp, newStamp ); /**/ + EXPECT( (oldStamp < newStamp) ); + /* Check to make sure callback is decrementing framesLeft. */ + oldFrames = myData.framesLeft; + Pa_Sleep(msec); + printf("oldFrames = %d, myData.framesLeft = %d\n", oldFrames, myData.framesLeft ); /**/ + EXPECT( (oldFrames > myData.framesLeft) ); + EXPECT( ((result=Pa_CloseStream( stream )) == 0) ); + stream = NULL; + } +error: + if( stream != NULL ) Pa_CloseStream( stream ); + return result; +} diff --git a/pd/portaudio/pa_tests/paqa_errs.c b/pd/portaudio/pa_tests/paqa_errs.c new file mode 100644 index 00000000..d0738602 --- /dev/null +++ b/pd/portaudio/pa_tests/paqa_errs.c @@ -0,0 +1,330 @@ +/* + * $Id: paqa_errs.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * paqa_devs.c + * Self Testing Quality Assurance app for PortAudio + * Do lots of bad things to test error reporting. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +/****************************************** Definitions ***********/ +#define MODE_INPUT (0) +#define MODE_OUTPUT (1) +#define FRAMES_PER_BUFFER (64) +#define SAMPLE_RATE (44100.0) +#define NUM_BUFFERS (0) +typedef struct PaQaData +{ + unsigned long framesLeft; + int numChannels; + int bytesPerSample; + int mode; +} +PaQaData; +/****************************************** Prototypes ***********/ +static void TestDevices( int mode ); +static void TestFormats( int mode, PaDeviceID deviceID, double sampleRate, + int numChannels ); +static int TestBadOpens( void ); +static int TestBadActions( void ); +static int QaCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +/****************************************** Globals ***********/ +static int gNumPassed = 0; +static int gNumFailed = 0; +/****************************************** Macros ***********/ +/* Print ERROR if it fails. Tally success or failure. */ +/* Odd do-while wrapper seems to be needed for some compilers. */ +#define EXPECT(_exp) \ + do \ + { \ + if ((_exp)) {\ + gNumPassed++; \ + } \ + else { \ + printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ + gNumFailed++; \ + goto error; \ + } \ + } while(0) +#define HOPEFOR(_exp) \ + do \ + { \ + if ((_exp)) {\ + gNumPassed++; \ + } \ + else { \ + printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ + gNumFailed++; \ + } \ + } while(0) +/*******************************************************************/ +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int QaCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + unsigned long i; + unsigned char *out = (unsigned char *) outputBuffer; + PaQaData *data = (PaQaData *) userData; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + /* Zero out buffer so we don't hear terrible noise. */ + if( data->mode == MODE_OUTPUT ) + { + unsigned long numBytes = framesPerBuffer * data->numChannels * data->bytesPerSample; + for( i=0; iframesLeft > framesPerBuffer ) + { + data->framesLeft -= framesPerBuffer; + return 0; + } + else + { + data->framesLeft = 0; + return 1; + } +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError result; + EXPECT( ((result=Pa_Initialize()) == 0) ); + TestBadOpens(); + TestBadActions(); +error: + Pa_Terminate(); + printf("QA Report: %d passed, %d failed.\n", gNumPassed, gNumFailed ); + return 0; +} +/*******************************************************************/ +static int TestBadOpens( void ) +{ + PortAudioStream *stream = NULL; + PaError result; + PaQaData myData; + /* Setup data for synthesis thread. */ + myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */ + myData.numChannels = 1; + myData.mode = MODE_OUTPUT; + HOPEFOR( (/* No devices specified. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + paNoDevice, 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); + HOPEFOR( ( /* Out of range input device specified. */ + (result = Pa_OpenStream( + &stream, + Pa_CountDevices(), 0, paFloat32, NULL, + paNoDevice, 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); + + HOPEFOR( ( /* Out of range output device specified. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_CountDevices(), 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); + HOPEFOR( ( /* Zero input channels. */ + (result = Pa_OpenStream( + &stream, + Pa_GetDefaultInputDeviceID(), 0, paFloat32, NULL, + paNoDevice, 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidChannelCount) ); + HOPEFOR( ( /* Zero output channels. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidChannelCount) ); + HOPEFOR( ( /* Nonzero input channels but no device. */ + (result = Pa_OpenStream( + &stream, + Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL, + paNoDevice, 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidChannelCount) ); + + HOPEFOR( ( /* Nonzero output channels but no device. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 2, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidChannelCount) ); + HOPEFOR( ( /* NULL stream pointer. */ + (result = Pa_OpenStream( + NULL, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paBadStreamPtr) ); + HOPEFOR( ( /* Low sample rate. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + 1.0, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidSampleRate) ); + HOPEFOR( ( /* High sample rate. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + 10000000.0, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidSampleRate) ); + HOPEFOR( ( /* NULL callback. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + NULL, + &myData ) + ) == paNullCallback) ); + HOPEFOR( ( /* Bad flag. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + (1<<3), + QaCallback, + &myData ) + ) == paInvalidFlag) ); + +#if 0 /* FIXME - this is legal for some implementations. */ + HOPEFOR( ( /* Use input device as output device. */ + (result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultInputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); + + HOPEFOR( ( /* Use output device as input device. */ + (result = Pa_OpenStream( + &stream, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + paNoDevice, 0, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == paInvalidDeviceId) ); +#endif + + if( stream != NULL ) Pa_CloseStream( stream ); + return result; +} +/*******************************************************************/ +static int TestBadActions( void ) +{ + PortAudioStream *stream = NULL; + PaError result; + PaQaData myData; + /* Setup data for synthesis thread. */ + myData.framesLeft = (unsigned long) (SAMPLE_RATE * 100); /* 100 seconds */ + myData.numChannels = 1; + myData.mode = MODE_OUTPUT; + /* Default output. */ + EXPECT( ((result = Pa_OpenStream( + &stream, + paNoDevice, 0, paFloat32, NULL, + Pa_GetDefaultOutputDeviceID(), 2, paFloat32, NULL, + SAMPLE_RATE, FRAMES_PER_BUFFER, NUM_BUFFERS, + paClipOff, + QaCallback, + &myData ) + ) == 0) ); + HOPEFOR( ((result = Pa_StartStream( NULL )) == paBadStreamPtr) ); + HOPEFOR( ((result = Pa_StopStream( NULL )) == paBadStreamPtr) ); + HOPEFOR( ((result = Pa_StreamActive( NULL )) == paBadStreamPtr) ); + HOPEFOR( ((result = Pa_CloseStream( NULL )) == paBadStreamPtr) ); + HOPEFOR( ((result = (PaError)Pa_StreamTime( NULL )) != 0) ); + HOPEFOR( ((result = (PaError)Pa_GetCPULoad( NULL )) != 0) ); +error: + if( stream != NULL ) Pa_CloseStream( stream ); + return result; +} diff --git a/pd/portaudio/pa_tests/patest1.c b/pd/portaudio/pa_tests/patest1.c new file mode 100644 index 00000000..1f969436 --- /dev/null +++ b/pd/portaudio/pa_tests/patest1.c @@ -0,0 +1,119 @@ +/* + $Id: patest1.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + patest1.c + Ring modulate the audio input with a sine wave for 20 seconds + using the Portable Audio api + Author: Ross Bencina + Modifications: + April 5th, 2001 - PLB - Check for NULL inputBuffer. +*/ +#include +#include +#include "portaudio.h" +#ifndef M_PI +#define M_PI (3.14159265) +#endif +typedef struct +{ + float sine[100]; + int phase; + int sampsToGo; +} +patest1data; +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long bufferFrames, + PaTime outTime, void *userData ) +{ + patest1data *data = (patest1data*)userData; + float *in = (float*)inputBuffer; + float *out = (float*)outputBuffer; + int framesToCalc = bufferFrames; + unsigned long i; + int finished = 0; + /* Check to see if any input data is available. */ + if(inputBuffer == NULL) return 0; + if( data->sampsToGo < bufferFrames ) + { + framesToCalc = data->sampsToGo; + finished = 1; + } + for( i=0; isine[data->phase]; /* left */ + *out++ = *in++ * data->sine[data->phase++]; /* right */ + if( data->phase >= 100 ) + data->phase = 0; + } + data->sampsToGo -= framesToCalc; + /* zero remainder of final buffer if not already done */ + for( ; i +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (1) +#define SAMPLE_RATE (44100) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) + +#define BUFFER_TABLE 9 +long buffer_table[] = {200,256,500,512,600, 723, 1000, 1024, 2345}; + +typedef struct +{ + short sine[TABLE_SIZE]; + int left_phase; + int right_phase; + unsigned int sampsToGo; +} +paTestData; +PaError TestOnce( int buffersize ); + +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + short *out = (short*)outputBuffer; + unsigned int i; + int finished = 0; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + if( data->sampsToGo < framesPerBuffer ) + { + /* final buffer... */ + + for( i=0; isampsToGo; i++ ) + { + *out++ = data->sine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + /* zero remainder of final buffer */ + for( ; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + data->sampsToGo -= framesPerBuffer; + } + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + int i; + PaError err; + printf("Test opening streams with different buffer sizes\n\n"); + + for (i = 0 ; i < BUFFER_TABLE; i++) + { + printf("Buffer size %d\n", buffer_table[i]); + err = TestOnce(buffer_table[i]); + if( err < 0 ) return 0; + + } +} + + +PaError TestOnce( int buffersize ) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int totalSamps; + /* initialise sinusoidal wavetable */ + for( i=0; i +#include +#include "portaudio.h" +#define NUM_SECONDS (4) +#define SAMPLE_RATE (44100) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct paTestData +{ + float sine[TABLE_SIZE]; + float amplitude; + int left_phase; + int right_phase; +} +paTestData; +PaError PlaySine( paTestData *data, unsigned long flags, float amplitude ); +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int sineCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float amplitude = data->amplitude; + unsigned int i; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + for( i=0; isine[data->left_phase]; /* left */ + *out++ = amplitude * data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError err; + paTestData DATA; + int i; + printf("PortAudio Test: output sine wave with and without clipping.\n"); + /* initialise sinusoidal wavetable */ + for( i=0; ileft_phase = data->right_phase = 0; + data->amplitude = amplitude; + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + 1024, + 0, /* number of buffers, if zero then use default minimum */ + flags, /* we won't output out of range samples so don't bother clipping them */ + sineCallback, + data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + Pa_Sleep( NUM_SECONDS * 1000 ); + printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) ); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + return paNoError; +error: + return err; +} diff --git a/pd/portaudio/pa_tests/patest_dither.c b/pd/portaudio/pa_tests/patest_dither.c new file mode 100644 index 00000000..2ab11e7b --- /dev/null +++ b/pd/portaudio/pa_tests/patest_dither.c @@ -0,0 +1,152 @@ +/* + * $Id: patest_dither.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_dither.c + * Attempt to hear difference between dithered and non-dithered signal. + * This only has an effect if the native format is 16 bit. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (4) +#define SAMPLE_RATE (44100) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct paTestData +{ + float sine[TABLE_SIZE]; + float amplitude; + int left_phase; + int right_phase; +} +paTestData; +PaError PlaySine( paTestData *data, PaStreamFlags flags, float amplitude ); +static int sineCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int sineCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float amplitude = data->amplitude; + unsigned int i; + (void) outTime; + (void) inputBuffer; + for( i=0; isine[data->left_phase]; /* left */ + *out++ = amplitude * data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError err; + paTestData DATA; + int i; + float amplitude = 32.0 / (1<<15); + printf("PortAudio Test: output EXTREMELY QUIET sine wave with and without dithering.\n"); + /* initialise sinusoidal wavetable */ + for( i=0; ileft_phase = data->right_phase = 0; + data->amplitude = amplitude; + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + 1024, + 0, /* number of buffers, if zero then use default minimum */ + flags, /* we won't output out of range samples so don't bother clipping them */ + sineCallback, + (void *)data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + Pa_Sleep( NUM_SECONDS * 1000 ); + printf("CPULoad = %8.6f\n", Pa_GetCPULoad( stream ) ); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + return paNoError; +error: + return err; +} diff --git a/pd/portaudio/pa_tests/patest_hang.c b/pd/portaudio/pa_tests/patest_hang.c new file mode 100644 index 00000000..b97727ba --- /dev/null +++ b/pd/portaudio/pa_tests/patest_hang.c @@ -0,0 +1,151 @@ +/* + * $Id: patest_hang.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * Play a sine then hang audio callback to test watchdog. + * + * Authors: + * Ross Bencina + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + + +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (1024) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TWOPI (M_PI * 2.0) + +typedef struct paTestData +{ + int sleepFor; + double phase; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + int finished = 0; + double phaseInc = 0.02; + double phase = data->phase; + + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( i=0; i TWOPI ) phase -= TWOPI; + /* This is not a very efficient way to calc sines. */ + *out++ = (float) sin( phase ); /* mono */ + } + + if( data->sleepFor > 0 ) + { + Pa_Sleep( data->sleepFor ); + } + + data->phase = phase; + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + int i; + paTestData data = {0}; + + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", + SAMPLE_RATE, FRAMES_PER_BUFFER ); + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 1, /* mono output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + +/* Gradually increase sleep time. */ + for( i=0; i<10000; i+= 1000 ) + { + printf("Sleep for %d milliseconds in audio callback.\n", i ); + data.sleepFor = i; + Pa_Sleep( ((i<1000) ? 1000 : i) ); + } + + printf("Suffer for 10 seconds.\n"); + Pa_Sleep( 10000 ); + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_latency.c b/pd/portaudio/pa_tests/patest_latency.c new file mode 100644 index 00000000..261a005c --- /dev/null +++ b/pd/portaudio/pa_tests/patest_latency.c @@ -0,0 +1,176 @@ +/* + * $Id: patest_latency.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * Hear the latency caused by big buffers. + * Play a sine wave and change frequency based on letter input. + * + * Author: Phil Burk , and Darren Gibbs + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDeviceID()) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (64) + +#if 0 +#define MIN_LATENCY_MSEC (2000) +#define NUM_BUFFERS ((MIN_LATENCY_MSEC * SAMPLE_RATE) / (FRAMES_PER_BUFFER * 1000)) +#else +#define NUM_BUFFERS (0) +#endif + +#define MIN_FREQ (100.0f) +#define CalcPhaseIncrement(freq) ((freq)/SAMPLE_RATE) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (400) +typedef struct +{ + float sine[TABLE_SIZE + 1]; // add one for guard point for interpolation + float phase_increment; + float left_phase; + float right_phase; +} +paTestData; +float LookupSine( paTestData *data, float phase ); +/* Convert phase between and 1.0 to sine value + * using linear interpolation. + */ +float LookupSine( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->sine[index]; + float hi = data->sine[index+1]; + float val = lo + fract*(hi-lo); + return val; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( i=0; ileft_phase); /* left */ + *out++ = LookupSine(data, data->right_phase); /* right */ + data->left_phase += data->phase_increment; + if( data->left_phase >= 1.0f ) data->left_phase -= 1.0f; + data->right_phase += (data->phase_increment * 1.5f); /* fifth above */ + if( data->right_phase >= 1.0f ) data->right_phase -= 1.0f; + } + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int done = 0; + printf("PortAudio Test: enter letter then hit ENTER. numBuffers = %d\n", NUM_BUFFERS ); + /* initialise sinusoidal wavetable */ + for( i=0; i + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (8) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (512) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; + int toggle; + int countDown; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( i=0; itoggle ) + { + *out++ = data->sine[data->left_phase]; /* left */ + *out++ = 0; /* right */ + } + else + { + *out++ = 0; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + } + + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + + if( data->countDown < 0 ) + { + data->countDown = SAMPLE_RATE; + data->toggle = !data->toggle; + } + data->countDown -= framesPerBuffer; + + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int timeout; + + printf("Play different tone sine waves that alternate between left and right channel.\n"); + printf("The low tone should be on the left channel.\n"); + + /* initialise sinusoidal wavetable */ + for( i=0; i 0 ) + { + Pa_Sleep( 300 ); + timeout -= 1; + } + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_longsine.c b/pd/portaudio/pa_tests/patest_longsine.c new file mode 100644 index 00000000..cc48c44a --- /dev/null +++ b/pd/portaudio/pa_tests/patest_longsine.c @@ -0,0 +1,137 @@ +/* + * $Id: patest_longsine.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_longsine.c + * Play a sine wave using the Portable Audio api until ENTER hit. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define SAMPLE_RATE (44100) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + return 0; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + printf("PortAudio Test: output sine wave.\n"); + + /* initialise sinusoidal wavetable */ + for( i=0; i +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (1) +#define SAMPLE_RATE (44100) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct +{ + short sine[TABLE_SIZE]; + int left_phase; + int right_phase; + unsigned int sampsToGo; +} +paTestData; +PaError TestOnce( void ); +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patest1Callback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + short *out = (short*)outputBuffer; + unsigned int i; + int finished = 0; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + if( data->sampsToGo < framesPerBuffer ) + { + /* final buffer... */ + + for( i=0; isampsToGo; i++ ) + { + *out++ = data->sine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + /* zero remainder of final buffer */ + for( ; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + data->sampsToGo -= framesPerBuffer; + } + return finished; +} +/*******************************************************************/ +#ifdef MACINTOSH +int main(void); +int main(void) +{ + int i; + PaError err; + int numLoops = 10; + printf("Loop %d times.\n", numLoops ); + for( i=0; i 1 ) + { + numLoops = atoi(argv[1]); + } + for( i=0; i + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define MAX_SINES (500) +#define MAX_USAGE (0.8) +#define SAMPLE_RATE (44100) +#define FREQ_TO_PHASE_INC(freq) (freq/(float)SAMPLE_RATE) + +#define MIN_PHASE_INC FREQ_TO_PHASE_INC(200.0f) +#define MAX_PHASE_INC (MIN_PHASE_INC * (1 << 5)) + +#define FRAMES_PER_BUFFER (512) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TWOPI (M_PI * 2.0) + +#define TABLE_SIZE (512) + +typedef struct paTestData +{ + int numSines; + float sine[TABLE_SIZE + 1]; /* add one for guard point for interpolation */ + float phases[MAX_SINES]; +} +paTestData; + +/* Convert phase between and 1.0 to sine value + * using linear interpolation. + */ +float LookupSine( paTestData *data, float phase ); +float LookupSine( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->sine[index]; + float hi = data->sine[index+1]; + float val = lo + fract*(hi-lo); + return val; +} + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float outSample; + float scaler; + int numForScale; + unsigned long i; + int j; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + +/* Detemine amplitude scaling factor */ + numForScale = data->numSines; + if( numForScale < 8 ) numForScale = 8; /* prevent pops at beginning */ + scaler = 1.0f / numForScale; + + for( i=0; inumSines; j++ ) + { + /* Advance phase of next oscillator. */ + phase = data->phases[j]; + phase += phaseInc; + if( phase >= 1.0 ) phase -= 1.0; + + output += LookupSine(data, phase); + data->phases[j] = phase; + + phaseInc *= 1.02f; + if( phaseInc > MAX_PHASE_INC ) phaseInc = MIN_PHASE_INC; + } + + outSample = (float) (output * scaler); + *out++ = outSample; /* Left */ + *out++ = outSample; /* Right */ + } + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + int i; + PaStream *stream; + PaError err; + paTestData data = {0}; + double load; + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); + + /* initialise sinusoidal wavetable */ + for( i=0; i +#include +#include "portaudio.h" + +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDevice()) +//#define NON_INTERLEAVED +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (0) // take what we get(256) +#define FREQ_INCR (300.0 / SAMPLE_RATE) +#define MAX_CHANNELS (64) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +typedef struct +{ + int numChannels; + double phases[MAX_CHANNELS]; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + float **outputs = (float**)outputBuffer; + +#ifdef NON_INTERLEAVED + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int frameIndex, channelIndex; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) + { + for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) + { + /* Output sine wave on every channel. */ + outputs[channelIndex][frameIndex] = (float) sin(data->phases[channelIndex]); + + /* Play each channel at a higher frequency. */ + data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); + if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); + } + } + +#else /* interleaved version */ + + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + int frameIndex, channelIndex; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( frameIndex=0; frameIndex<(int)framesPerBuffer; frameIndex++ ) + { + for( channelIndex=0; channelIndexnumChannels; channelIndex++ ) + { + /* Output sine wave on every channel. */ + *out++ = (float) sin(data->phases[channelIndex]); + + /* Play each channel at a higher frequency. */ + data->phases[channelIndex] += FREQ_INCR * (4 + channelIndex); + if( data->phases[channelIndex] >= (2.0 * M_PI) ) data->phases[channelIndex] -= (2.0 * M_PI); + } + } +#endif /* NON_INTERLEAVED */ + return 0; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStream *stream; + PaError err; + const PaDeviceInfo *pdi; + paTestData data = {0}; + printf("PortAudio Test: output sine wave on each channel.\n" ); + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + pdi = Pa_GetDeviceInfo( OUTPUT_DEVICE ); + data.numChannels = pdi->maxOutputChannels; + if( data.numChannels > MAX_CHANNELS ) data.numChannels = MAX_CHANNELS; + printf("Number of Channels = %d\n", data.numChannels ); + + err = Pa_OpenStream( + &stream, + paNoDevice, /* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + 0, /* default input latency */ + NULL, + OUTPUT_DEVICE, + data.numChannels, +#ifdef NON_INTERLEAVED + paFloat32 | paNonInterleaved, /* 32 bit floating point output */ +#else + paFloat32, +#endif + 0, /* default output latency */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Hit ENTER to stop sound.\n"); + getchar(); + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + Pa_CloseStream( stream ); + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_pink.c b/pd/portaudio/pa_tests/patest_pink.c new file mode 100644 index 00000000..7a3e29cd --- /dev/null +++ b/pd/portaudio/pa_tests/patest_pink.c @@ -0,0 +1,245 @@ +/* + * $Id: patest_pink.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + patest_pink.c + Generate Pink Noise using Gardner method. + Optimization suggested by James McCartney uses a tree + to select which random value to replace. + x x x x x x x x x x x x x x x x + x x x x x x x x + x x x x + x x + x + Tree is generated by counting trailing zeros in an increasing index. + When the index is zero, no random number is selected. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define PINK_MAX_RANDOM_ROWS (30) +#define PINK_RANDOM_BITS (24) +#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS) +typedef struct +{ + long pink_Rows[PINK_MAX_RANDOM_ROWS]; + long pink_RunningSum; /* Used to optimize summing of generators. */ + int pink_Index; /* Incremented each sample. */ + int pink_IndexMask; /* Index wrapped by ANDing with this mask. */ + float pink_Scalar; /* Used to scale within range of -1.0 to +1.0 */ +} +PinkNoise; +/* Prototypes */ +static unsigned long GenerateRandomNumber( void ); +void InitializePinkNoise( PinkNoise *pink, int numRows ); +float GeneratePinkNoise( PinkNoise *pink ); +/************************************************************/ +/* Calculate pseudo-random 32 bit number based on linear congruential method. */ +static unsigned long GenerateRandomNumber( void ) +{ + /* Change this seed for different random sequences. */ + static unsigned long randSeed = 22222; + randSeed = (randSeed * 196314165) + 907633515; + return randSeed; +} +/************************************************************/ +/* Setup PinkNoise structure for N rows of generators. */ +void InitializePinkNoise( PinkNoise *pink, int numRows ) +{ + int i; + long pmax; + pink->pink_Index = 0; + pink->pink_IndexMask = (1<pink_Scalar = 1.0f / pmax; + /* Initialize rows. */ + for( i=0; ipink_Rows[i] = 0; + pink->pink_RunningSum = 0; +} +#define PINK_MEASURE +#ifdef PINK_MEASURE +float pinkMax = -999.0; +float pinkMin = 999.0; +#endif +/* Generate Pink noise values between -1.0 and +1.0 */ +float GeneratePinkNoise( PinkNoise *pink ) +{ + long newRandom; + long sum; + float output; + /* Increment and mask index. */ + pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask; + /* If index is zero, don't update any random values. */ + if( pink->pink_Index != 0 ) + { + /* Determine how many trailing zeros in PinkIndex. */ + /* This algorithm will hang if n==0 so test first. */ + int numZeros = 0; + int n = pink->pink_Index; + while( (n & 1) == 0 ) + { + n = n >> 1; + numZeros++; + } + /* Replace the indexed ROWS random value. + * Subtract and add back to RunningSum instead of adding all the random + * values together. Only one changes each time. + */ + pink->pink_RunningSum -= pink->pink_Rows[numZeros]; + newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; + pink->pink_RunningSum += newRandom; + pink->pink_Rows[numZeros] = newRandom; + } + + /* Add extra white noise value. */ + newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT; + sum = pink->pink_RunningSum + newRandom; + /* Scale to range of -1.0 to 0.9999. */ + output = pink->pink_Scalar * sum; +#ifdef PINK_MEASURE + /* Check Min/Max */ + if( output > pinkMax ) pinkMax = output; + else if( output < pinkMin ) pinkMin = output; +#endif + return output; +} +/*******************************************************************/ +#define PINK_TEST +#ifdef PINK_TEST +/* Context for callback routine. */ +typedef struct +{ + PinkNoise leftPink; + PinkNoise rightPink; + unsigned int sampsToGo; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + int finished; + int i; + int numFrames; + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + (void) inputBuffer; /* Prevent "unused variable" warnings. */ + (void) outTime; + + /* Are we almost at end. */ + if( data->sampsToGo < framesPerBuffer ) + { + numFrames = data->sampsToGo; + finished = 1; + } + else + { + numFrames = framesPerBuffer; + finished = 0; + } + for( i=0; ileftPink ); + *out++ = GeneratePinkNoise( &data->rightPink ); + } + data->sampsToGo -= numFrames; + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int totalSamps; + /* Initialize two pink noise signals with different numbers of rows. */ + InitializePinkNoise( &data.leftPink, 12 ); + InitializePinkNoise( &data.rightPink, 16 ); + /* Look at a few values. */ + { + int i; + float pink; + for( i=0; i<20; i++ ) + { + pink = GeneratePinkNoise( &data.leftPink ); + printf("Pink = %f\n", pink ); + } + } + data.sampsToGo = totalSamps = 8*44100; /* Play for a few seconds. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + /* Open a stereo PortAudio stream so we can hear the result. */ + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + NULL, + 44100., + 2048, /* 46 msec buffers */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + printf("Waiting for sound to finish.\n"); + while( Pa_StreamActive( stream ) ) + { + Pa_Sleep(100); /* SPIN! */ + } + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; +#ifdef PINK_MEASURE + printf("Pink min = %f, max = %f\n", pinkMin, pinkMax ); +#endif + Pa_Terminate(); + return 0; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return 0; +} +#endif /* PINK_TEST */ diff --git a/pd/portaudio/pa_tests/patest_record.c b/pd/portaudio/pa_tests/patest_record.c new file mode 100644 index 00000000..59471aaf --- /dev/null +++ b/pd/portaudio/pa_tests/patest_record.c @@ -0,0 +1,327 @@ +/* + * $Id: patest_record.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_record.c + * Record input into an array. + * Save array to a file. + * Playback recorded data. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include "portaudio.h" + +/* #define SAMPLE_RATE (17932) /* Test failure to open with this value. */ +#define SAMPLE_RATE (44100) +#define NUM_SECONDS (5) +#define NUM_CHANNELS (2) +/* #define DITHER_FLAG (paDitherOff) /**/ +#define DITHER_FLAG (0) /**/ + +/* Select sample format. */ +#if 1 +#define PA_SAMPLE_TYPE paFloat32 +typedef float SAMPLE; +#define SAMPLE_SILENCE (0.0f) +#elif 1 +#define PA_SAMPLE_TYPE paInt16 +typedef short SAMPLE; +#define SAMPLE_SILENCE (0) +#elif 0 +#define PA_SAMPLE_TYPE paInt8 +typedef char SAMPLE; +#define SAMPLE_SILENCE (0) +#else +#define PA_SAMPLE_TYPE paUInt8 +typedef unsigned char SAMPLE; +#define SAMPLE_SILENCE (128) + +#endif + +typedef struct +{ + int frameIndex; /* Index into sample array. */ + int maxFrameIndex; + SAMPLE *recordedSamples; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int recordCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = (SAMPLE*)inputBuffer; + SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; + long framesToCalc; + long i; + int finished; + unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; + + (void) outputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + framesToCalc = framesLeft; + finished = paComplete; + } + else + { + framesToCalc = framesPerBuffer; + finished = paContinue; + } + + if( inputBuffer == NULL ) + { + for( i=0; iframeIndex += framesToCalc; + return finished; +} + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int playCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS]; + SAMPLE *wptr = (SAMPLE*)outputBuffer; + unsigned int i; + int finished; + unsigned int framesLeft = data->maxFrameIndex - data->frameIndex; + (void) inputBuffer; /* Prevent unused variable warnings. */ + (void) outTime; + + if( framesLeft < framesPerBuffer ) + { + /* final buffer... */ + for( i=0; iframeIndex += framesLeft; + finished = paComplete; + } + else + { + for( i=0; iframeIndex += framesPerBuffer; + finished = paContinue; + } + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStream *stream; + PaError err; + paTestData data; + int i; + int totalFrames; + int numSamples; + int numBytes; + SAMPLE max, average, val; + printf("patest_record.c\n"); fflush(stdout); + + data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; /* Record for a few seconds. */ + data.frameIndex = 0; + numSamples = totalFrames * NUM_CHANNELS; + + numBytes = numSamples * sizeof(SAMPLE); + data.recordedSamples = (SAMPLE *) malloc( numBytes ); + if( data.recordedSamples == NULL ) + { + printf("Could not allocate record array.\n"); + exit(1); + } + for( i=0; i max ) + { + max = val; + } + average += val; + } + + average = average / numSamples; + + if( PA_SAMPLE_TYPE == paFloat32 ) + { + printf("sample max amplitude = %f\n", max ); + printf("sample average = %f\n", average ); + } + else + { + printf("sample max amplitude = %d\n", max ); + printf("sample average = %d\n", average ); + } + + /* Write recorded data to a file. */ +#if 0 + { + FILE *fid; + fid = fopen("recorded.raw", "wb"); + if( fid == NULL ) + { + printf("Could not open file."); + } + else + { + fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid ); + fclose( fid ); + printf("Wrote data to 'recorded.raw'\n"); + } + } +#endif + + /* Playback recorded data. -------------------------------------------- */ + data.frameIndex = 0; + printf("Begin playback.\n"); fflush(stdout); + err = Pa_OpenStream( + &stream, + paNoDevice, + 0, /* NO input */ + PA_SAMPLE_TYPE, + 0, /* default latency */ + NULL, + Pa_GetDefaultOutputDevice(), + NUM_CHANNELS, /* stereo output */ + PA_SAMPLE_TYPE, + 0, /* default latency */ + NULL, + SAMPLE_RATE, + 1024, /* frames per buffer */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + playCallback, + &data ); + if( err != paNoError ) goto error; + + if( stream ) + { + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + printf("Waiting for playback to finish.\n"); fflush(stdout); + + while( Pa_IsStreamActive( stream ) ) Pa_Sleep(100); + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + printf("Done.\n"); fflush(stdout); + } + free( data.recordedSamples ); + + Pa_Terminate(); + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return -1; +} diff --git a/pd/portaudio/pa_tests/patest_ringmix.c b/pd/portaudio/pa_tests/patest_ringmix.c new file mode 100644 index 00000000..34c66381 --- /dev/null +++ b/pd/portaudio/pa_tests/patest_ringmix.c @@ -0,0 +1,41 @@ +/* $Id: patest_ringmix.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ */ + +#include "stdio.h" +#include "portaudio.h" +/* This will be called asynchronously by the PortAudio engine. */ +static int myCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, PaTimestamp outTime, void *userData ) +{ + float *out = (float *) outputBuffer; + float *in = (float *) inputBuffer; + float leftInput, rightInput; + unsigned int i; + if( inputBuffer == NULL ) return 0; + /* Read input buffer, process data, and fill output buffer. */ + for( i=0; i +#include +#include "portaudio.h" +#define NUM_SECONDS (4) +#define SAMPLE_RATE (44100) +typedef struct +{ + float left_phase; + float right_phase; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + /* Cast data passed through stream to our structure. */ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( i=0; ileft_phase; /* left */ + *out++ = data->right_phase; /* right */ + /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */ + data->left_phase += 0.01f; + /* When signal reaches top, drop back down. */ + if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f; + /* higher pitch so we can distinguish left and right. */ + data->right_phase += 0.03f; + if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f; + } + return 0; +} +/*******************************************************************/ +static paTestData data; +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + printf("PortAudio Test: output sawtooth wave.\n"); + /* Initialize our data for use by callback. */ + data.left_phase = data.right_phase = 0.0; + /* Initialize library before making any other calls. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + /* Open an audio I/O stream. */ + err = Pa_OpenDefaultStream( + &stream, + 0, /* no input channels */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + SAMPLE_RATE, + 256, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + /* Sleep for several seconds. */ + Pa_Sleep(NUM_SECONDS*1000); + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_sine.c b/pd/portaudio/pa_tests/patest_sine.c new file mode 100644 index 00000000..1bebb90e --- /dev/null +++ b/pd/portaudio/pa_tests/patest_sine.c @@ -0,0 +1,152 @@ +/* + * $Id: patest_sine.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_sine.c + * Play a sine wave using the Portable Audio api for several seconds. + * + * Authors: + * Ross Bencina + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define NUM_SECONDS (5) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (64) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + + (void) timeInfo; /* Prevent unused variable warnings. */ + (void) statusFlags; + (void) inputBuffer; + + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + + return paContinue; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStreamParameters outputParameters; + PaStream *stream; + PaError err; + paTestData data; + int i; + + + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); + + /* initialise sinusoidal wavetable */ + for( i=0; idefaultLowOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream( + &stream, + NULL, /* no input */ + &outputParameters, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Play for %d seconds.\n", NUM_SECONDS ); + Pa_Sleep( NUM_SECONDS * 1000 ); + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + Pa_Terminate(); + printf("Test finished.\n"); + + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_sine8.c b/pd/portaudio/pa_tests/patest_sine8.c new file mode 100644 index 00000000..f3ef9ebb --- /dev/null +++ b/pd/portaudio/pa_tests/patest_sine8.c @@ -0,0 +1,184 @@ +/* + * $Id: patest_sine8.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_sine8.c + * Play a sine wave using the Portable Audio api for several seconds. + * Test 8 bit data. + * + * Author: Ross Bencina + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define NUM_SECONDS (8) +#define SAMPLE_RATE (44100) +#define TEST_UNSIGNED (1) +#if TEST_UNSIGNED +#define TEST_FORMAT paUInt8 +#else +#define TEST_FORMAT paInt8 +#endif +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (200) +typedef struct +{ +#if TEST_UNSIGNED + unsigned char sine[TABLE_SIZE]; +#else + char sine[TABLE_SIZE]; +#endif + int left_phase; + int right_phase; + unsigned int framesToGo; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + char *out = (char*)outputBuffer; + int i; + int framesToCalc; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( data->framesToGo < framesPerBuffer ) + { + framesToCalc = data->framesToGo; + data->framesToGo = 0; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + data->framesToGo -= framesPerBuffer; + } + + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + /* zero remainder of final buffer */ + for( ; i<(int)framesPerBuffer; i++ ) + { +#if TEST_UNSIGNED + *out++ = (unsigned char) 0x80; /* left */ + *out++ = (unsigned char) 0x80; /* right */ +#else + *out++ = 0; /* left */ + *out++ = 0; /* right */ +#endif + + } + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + int totalSamps; +#if TEST_UNSIGNED + printf("PortAudio Test: output UNsigned 8 bit sine wave.\n"); +#else + printf("PortAudio Test: output signed 8 bit sine wave.\n"); +#endif + /* initialise sinusoidal wavetable */ + for( i=0; i +#include +#include "portaudio.h" + +#define NUM_SECONDS (10) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (512) +#define LEFT_FREQ (SAMPLE_RATE/256.0) /* So we hit 1.0 */ +#define RIGHT_FREQ (500.0) +#define AMPLITUDE (1.0) + +/* Select ONE format for testing. */ +#define TEST_UINT8 (0) +#define TEST_INT8 (0) +#define TEST_INT16 (1) +#define TEST_FLOAT32 (0) + +#if TEST_UINT8 +#define TEST_FORMAT paUInt8 +typedef unsigned char SAMPLE_t; +#define SAMPLE_ZERO (0x80) +#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x))) +#define FORMAT_NAME "Unsigned 8 Bit" + +#elif TEST_INT8 +#define TEST_FORMAT paInt8 +typedef char SAMPLE_t; +#define SAMPLE_ZERO (0) +#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(127.0 * (x))) +#define FORMAT_NAME "Signed 8 Bit" + +#elif TEST_INT16 +#define TEST_FORMAT paInt16 +typedef short SAMPLE_t; +#define SAMPLE_ZERO (0) +#define DOUBLE_TO_SAMPLE(x) (SAMPLE_ZERO + (SAMPLE_t)(32767 * (x))) +#define FORMAT_NAME "Signed 16 Bit" + +#elif TEST_FLOAT32 +#define TEST_FORMAT paFloat32 +typedef float SAMPLE_t; +#define SAMPLE_ZERO (0.0) +#define DOUBLE_TO_SAMPLE(x) ((SAMPLE_t)(x)) +#define FORMAT_NAME "Float 32 Bit" +#endif + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + + +typedef struct +{ + double left_phase; + double right_phase; + unsigned int framesToGo; +} +paTestData; +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + SAMPLE_t *out = (SAMPLE_t *)outputBuffer; + int i; + int framesToCalc; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + if( data->framesToGo < framesPerBuffer ) + { + framesToCalc = data->framesToGo; + data->framesToGo = 0; + finished = 1; + } + else + { + framesToCalc = framesPerBuffer; + data->framesToGo -= framesPerBuffer; + } + + for( i=0; ileft_phase += (LEFT_FREQ / SAMPLE_RATE); + if( data->left_phase > 1.0) data->left_phase -= 1.0; + *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->left_phase * M_PI * 2. ))); + + data->right_phase += (RIGHT_FREQ / SAMPLE_RATE); + if( data->right_phase > 1.0) data->right_phase -= 1.0; + *out++ = DOUBLE_TO_SAMPLE( AMPLITUDE * sin( (data->right_phase * M_PI * 2. ))); + } + /* zero remainder of final buffer */ + for( ; i<(int)framesPerBuffer; i++ ) + { + *out++ = SAMPLE_ZERO; /* left */ + *out++ = SAMPLE_ZERO; /* right */ + } + return finished; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStream *stream; + PaError err; + paTestData data; + int totalSamps; + + printf("PortAudio Test: output " FORMAT_NAME "\n"); + + + data.left_phase = data.right_phase = 0.0; + data.framesToGo = totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + TEST_FORMAT, + 0, /* default latency */ + NULL, + Pa_GetDefaultOutputDevice(), /* default output device */ + 2, /* stereo output */ + TEST_FORMAT, + 0, /* default latency */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Waiting %d seconds for sound to finish.\n", NUM_SECONDS ); + while( Pa_IsStreamActive( stream ) ) Pa_Sleep(10); + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + + printf("PortAudio Test Finished: " FORMAT_NAME "\n"); + + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_sine_time.c b/pd/portaudio/pa_tests/patest_sine_time.c new file mode 100644 index 00000000..c849af0a --- /dev/null +++ b/pd/portaudio/pa_tests/patest_sine_time.c @@ -0,0 +1,194 @@ +/* + * $Id: patest_sine_time.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_sine_time.c + * Play a sine wave using the Portable Audio api for several seconds. + * Pausing in the middle. + * use the Pa_GetStreamTime() and Pa_IsStreamActive() calls. + * + * Authors: + * Ross Bencina + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#include "pa_util.h" +#define NUM_SECONDS (8) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (64) +#define NUM_BUFFERS (0) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TWOPI (M_PI * 2.0) + +#define TABLE_SIZE (200) +typedef struct +{ + double left_phase; + double right_phase; + volatile PaTimestamp outTime; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + + double left_phaseInc = 0.02; + double right_phaseInc = 0.06; + + double left_phase = data->left_phase; + double right_phase = data->right_phase; + + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + data->outTime = outTime;\ + + for( i=0; i TWOPI ) left_phase -= TWOPI; + *out++ = (float) sin( left_phase ); + + right_phase += right_phaseInc; + if( right_phase > TWOPI ) right_phase -= TWOPI; + *out++ = (float) sin( right_phase ); + } + + data->left_phase = left_phase; + data->right_phase = right_phase; + + return paContinue; +} +/*******************************************************************/ +static void ReportStreamTime( PaStream *stream, paTestData *data ); +static void ReportStreamTime( PaStream *stream, paTestData *data ) +{ + PaTimestamp streamTime, latency, outTime; + + streamTime = Pa_GetStreamTime( stream ); + outTime = data->outTime; + if( outTime < 0.0 ) + { + printf("Stream time = %8.1f\n", streamTime ); + } + else + { + latency = outTime - streamTime; + printf("Stream time = %8.1f, outTime = %8.1f, latency = %8.1f\n", + streamTime, outTime, latency ); + } + fflush(stdout); +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStream *stream; + PaError err; + paTestData DATA; + int totalSamps; + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); + DATA.left_phase = DATA.right_phase = 0; + totalSamps = NUM_SECONDS * SAMPLE_RATE; /* Play for a few seconds. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + 0, /* default latency */ + NULL, + Pa_GetDefaultOutputDevice(), /* default output device */ + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + 0, /* default latency */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &DATA ); + if( err != paNoError ) goto error; + + /* Watch until sound is halfway finished. */ + printf("Play for %d seconds.\n", NUM_SECONDS/2 ); fflush(stdout); + + DATA.outTime = -1.0; // mark time for callback as undefined + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + do + { + ReportStreamTime( stream, &DATA ); + Pa_Sleep(100); + } while( Pa_GetStreamTime( stream ) < (totalSamps/2) ); + + /* Stop sound until ENTER hit. */ + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + printf("Pause for 2 seconds.\n"); fflush(stdout); + Pa_Sleep( 2000 ); + + DATA.outTime = -1.0; // mark time for callback as undefined + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Play until sound is finished.\n"); fflush(stdout); + do + { + ReportStreamTime( stream, &DATA ); + Pa_Sleep(100); + } while( Pa_GetStreamTime( stream ) < (totalSamps/2) ); + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_start_stop.c b/pd/portaudio/pa_tests/patest_start_stop.c new file mode 100644 index 00000000..0e183708 --- /dev/null +++ b/pd/portaudio/pa_tests/patest_start_stop.c @@ -0,0 +1,160 @@ +/* + * $Id: patest_start_stop.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_start_stop.c + * Play a sine wave using the Portable Audio api for several seconds. + * Start and stop the stream multiple times. + * + * Authors: + * Ross Bencina + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define NUM_SECONDS (3) +#define NUM_LOOPS (4) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (400) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, + void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + + (void) timeInfo; /* Prevent unused variable warnings. */ + (void) statusFlags; + (void) inputBuffer; + + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + + return paContinue; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStreamParameters outputParameters; + PaStream *stream; + PaError err; + paTestData data; + int i; + + + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); + + /* initialise sinusoidal wavetable */ + for( i=0; idefaultLowOutputLatency; + outputParameters.hostApiSpecificStreamInfo = NULL; + + err = Pa_OpenStream( + &stream, + NULL, /* no input */ + &outputParameters, + SAMPLE_RATE, + FRAMES_PER_BUFFER, + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + + for( i=0; i + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDevice()) +#define SLEEP_DUR (200) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (256) +#define LATENCY_MSEC (3000) +#define FRAMES_PER_NOTE (SAMPLE_RATE/2) +#define MAX_REPEATS (2) +#define FUNDAMENTAL (400.0f / SAMPLE_RATE) +#define NOTE_0 (FUNDAMENTAL * 1.0f / 1.0f) +#define NOTE_1 (FUNDAMENTAL * 5.0f / 4.0f) +#define NOTE_2 (FUNDAMENTAL * 4.0f / 3.0f) +#define NOTE_3 (FUNDAMENTAL * 3.0f / 2.0f) +#define NOTE_4 (FUNDAMENTAL * 2.0f / 1.0f) +#define MODE_FINISH (0) +#define MODE_STOP (1) +#define MODE_ABORT (2) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TABLE_SIZE (400) +typedef struct +{ + float waveform[TABLE_SIZE + 1]; // add one for guard point for interpolation + float phase_increment; + float phase; + float *tune; + int notesPerTune; + int frameCounter; + int noteCounter; + int repeatCounter; + PaTimestamp outTime; + int stopMode; + int done; +} +paTestData; +/************* Prototypes *****************************/ +int TestStopMode( paTestData *data ); +float LookupWaveform( paTestData *data, float phase ); +/****************************************************** + * Convert phase between 0.0 and 1.0 to waveform value + * using linear interpolation. + */ +float LookupWaveform( paTestData *data, float phase ) +{ + float fIndex = phase*TABLE_SIZE; + int index = (int) fIndex; + float fract = fIndex - index; + float lo = data->waveform[index]; + float hi = data->waveform[index+1]; + float val = lo + fract*(hi-lo); + return val; +} +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + float value; + unsigned int i = 0; + int finished = paContinue; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + data->outTime = outTime; + if( !data->done ) + { + for( i=0; iframeCounter >= FRAMES_PER_NOTE ) + { + data->noteCounter += 1; + data->frameCounter = 0; + /* Are we done with this tune? */ + if( data->noteCounter >= data->notesPerTune ) + { + data->noteCounter = 0; + data->repeatCounter += 1; + /* Are we totally done? */ + if( data->repeatCounter >= MAX_REPEATS ) + { + data->done = 1; + if( data->stopMode == MODE_FINISH ) + { + finished = paComplete; + break; + } + } + } + data->phase_increment = data->tune[data->noteCounter]; + } + value = LookupWaveform(data, data->phase); + *out++ = value; /* left */ + *out++ = value; /* right */ + data->phase += data->phase_increment; + if( data->phase >= 1.0f ) data->phase -= 1.0f; + + data->frameCounter += 1; + } + } + /* zero remainder of final buffer */ + for( ; idone = 0; + data->phase = 0.0; + data->frameCounter = 0; + data->noteCounter = 0; + data->repeatCounter = 0; + data->phase_increment = data->tune[data->noteCounter]; + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + 0, + NULL, + OUTPUT_DEVICE, + 2, /* stereo output */ + paFloat32, /* 32 bit floating point output */ + LATENCY_MSEC, + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + if( data->stopMode == MODE_FINISH ) + { + while( Pa_IsStreamActive( stream ) ) + { + /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime, + data->noteCounter, data->repeatCounter ); + fflush(stdout); /**/ + Pa_Sleep( SLEEP_DUR ); + } + } + else + { + while( data->repeatCounter < MAX_REPEATS ) + { + /*printf("outTime = %g, note# = %d, repeat# = %d\n", data->outTime, + data->noteCounter, data->repeatCounter ); + fflush(stdout); /**/ + Pa_Sleep( SLEEP_DUR ); + } + } + + if( data->stopMode == MODE_ABORT ) + { + printf("Call Pa_AbortStream()\n"); + err = Pa_AbortStream( stream ); + } + else + { + printf("Call Pa_StopStream()\n"); + err = Pa_StopStream( stream ); + } + if( err != paNoError ) goto error; + + printf("Call Pa_CloseStream()\n"); fflush(stdout); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_sync.c b/pd/portaudio/pa_tests/patest_sync.c new file mode 100644 index 00000000..46854eb6 --- /dev/null +++ b/pd/portaudio/pa_tests/patest_sync.c @@ -0,0 +1,257 @@ +/* + * $Id: patest_sync.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * patest_sync.c + * Test time stamping and synchronization of audio and video. + * A high latency is used so we can hear the difference in time. + * Random durations are used so we know we are hearing the right beep + * and not the one before or after. + * + * Sequence of events: + * Foreground requests a beep. + * Background randomly schedules a beep. + * Foreground waits for the beep to be heard based on PaUtil_GetTime(). + * Foreground outputs video (printf) in sync with audio. + * Repeat. + * + * Author: Phil Burk http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.portaudio.com + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" +#include "pa_util.h" +#define NUM_BEEPS (6) +#define SAMPLE_RATE (44100) +#define SAMPLE_PERIOD (1.0/44100.0) +#define FRAMES_PER_BUFFER (256) +#define BEEP_DURATION (400) +#define LATENCY_MSEC (2000) +#define SLEEP_MSEC (10) +#define TIMEOUT_MSEC (15000) + +#define STATE_BKG_IDLE (0) +#define STATE_BKG_PENDING (1) +#define STATE_BKG_BEEPING (2) +typedef struct +{ + float left_phase; + float right_phase; + int state; + int requestBeep; /* Set by foreground, cleared by background. */ + PaTime beepTime; + int beepCount; + double latency; /* For debugging. */ +} +paTestData; + +static unsigned long GenerateRandomNumber( void ); +/************************************************************/ +/* Calculate pseudo-random 32 bit number based on linear congruential method. */ +static unsigned long GenerateRandomNumber( void ) +{ + static unsigned long randSeed = 99887766; /* Change this for different random sequences. */ + randSeed = (randSeed * 196314165) + 907633515; + return randSeed; +} + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *timeInfo, + PaStreamCallbackFlags statusFlags, void *userData ) +{ + /* Cast data passed through stream to our structure. */ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned int i; + (void) inputBuffer; + + data->latency = timeInfo->outputBufferDacTime - timeInfo->currentTime; + + for( i=0; istate ) + { + case STATE_BKG_IDLE: + /* Schedule beep at some random time in the future. */ + if( data->requestBeep ) + { + int random = GenerateRandomNumber() >> 14; + data->beepTime = timeInfo->outputBufferDacTime + (( (double)(random + SAMPLE_RATE)) * SAMPLE_PERIOD ); + data->state = STATE_BKG_PENDING; + } + *out++ = 0.0; /* left */ + *out++ = 0.0; /* right */ + break; + + case STATE_BKG_PENDING: + if( (timeInfo->outputBufferDacTime + (i*SAMPLE_PERIOD)) >= data->beepTime ) + { + data->state = STATE_BKG_BEEPING; + data->beepCount = BEEP_DURATION; + data->left_phase = data->right_phase = 0.0; + } + *out++ = 0.0; /* left */ + *out++ = 0.0; /* right */ + break; + + case STATE_BKG_BEEPING: + if( data->beepCount <= 0 ) + { + data->state = STATE_BKG_IDLE; + data->requestBeep = 0; + *out++ = 0.0; /* left */ + *out++ = 0.0; /* right */ + } + else + { + /* Play sawtooth wave. */ + *out++ = data->left_phase; /* left */ + *out++ = data->right_phase; /* right */ + /* Generate simple sawtooth phaser that ranges between -1.0 and 1.0. */ + data->left_phase += 0.01f; + /* When signal reaches top, drop back down. */ + if( data->left_phase >= 1.0f ) data->left_phase -= 2.0f; + /* higher pitch so we can distinguish left and right. */ + data->right_phase += 0.03f; + if( data->right_phase >= 1.0f ) data->right_phase -= 2.0f; + } + data->beepCount -= 1; + break; + + default: + data->state = STATE_BKG_IDLE; + break; + } + } + return 0; +} +/*******************************************************************/ +int main(void); +int main(void) +{ + PaStream *stream; + PaError err; + paTestData DATA; + int i, timeout; + PaTime previousTime; + PaStreamParameters outputParameters; + printf("PortAudio Test: you should see BEEP at the same time you hear it.\n"); + printf("Wait for a few seconds random delay between BEEPs.\n"); + printf("BEEP %d times.\n", NUM_BEEPS ); + /* Initialize our DATA for use by callback. */ + DATA.left_phase = DATA.right_phase = 0.0; + DATA.state = STATE_BKG_IDLE; + DATA.requestBeep = 0; + /* Initialize library before making any other calls. */ + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + outputParameters.device = Pa_GetDefaultOutputDevice(); + outputParameters.channelCount = 2; + outputParameters.hostApiSpecificStreamInfo = NULL; + outputParameters.sampleFormat = paFloat32; + outputParameters.suggestedLatency = (double)LATENCY_MSEC / 1000; + + /* Open an audio I/O stream. */ + err = Pa_OpenStream( + &stream, + NULL, /* no input */ + &outputParameters, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &DATA ); + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("started\n"); + fflush(stdout); + + previousTime = Pa_GetStreamTime( stream ); + for( i=0; i 0 ) ) Pa_Sleep(SLEEP_MSEC); + if( timeout <= 0 ) + { + fprintf( stderr, "Timed out waiting for background to acknowledge request.\n" ); + goto error; + } + printf("calc beep for %9.3f, latency = %6.3f\n", DATA.beepTime, DATA.latency ); + fflush(stdout); + + /* Wait for scheduled beep time. */ + timeout = TIMEOUT_MSEC + (10000/SLEEP_MSEC); + while( (Pa_GetStreamTime( stream ) < DATA.beepTime) && (timeout-- > 0 ) ) + { + Pa_Sleep(SLEEP_MSEC); + } + if( timeout <= 0 ) + { + fprintf( stderr, "Timed out waiting for time. Now = %9.3f, Beep for %9.3f.\n", + PaUtil_GetTime(), DATA.beepTime ); + goto error; + } + + /* Beep should be sounding now so print synchronized BEEP. */ + printf("hear \"BEEP\" at %9.3f, delta = %9.3f\n", + Pa_GetStreamTime( stream ), (DATA.beepTime - previousTime) ); + fflush(stdout); + + previousTime = DATA.beepTime; + } + + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_toomanysines.c b/pd/portaudio/pa_tests/patest_toomanysines.c new file mode 100644 index 00000000..2fbb8433 --- /dev/null +++ b/pd/portaudio/pa_tests/patest_toomanysines.c @@ -0,0 +1,172 @@ +/* + * $Id: patest_toomanysines.c,v 1.1.1.1 2003-05-09 16:03:56 ggeiger Exp $ + * Play more sine waves than we can handle in real time as a stress test, + * + * Authors: + * Ross Bencina + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define MAX_SINES (500) +#define MAX_LOAD (1.2) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (512) +#ifndef M_PI +#define M_PI (3.14159265) +#endif +#define TWOPI (M_PI * 2.0) + +typedef struct paTestData +{ + int numSines; + double phases[MAX_SINES]; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + int j; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + + for( i=0; inumSines; j++ ) + { + /* Advance phase of next oscillator. */ + phase = data->phases[j]; + phase += phaseInc; + if( phase > TWOPI ) phase -= TWOPI; + + phaseInc *= 1.02; + if( phaseInc > 0.5 ) phaseInc *= 0.5; + + /* This is not a very efficient way to calc sines. */ + output += (float) sin( phase ); + data->phases[j] = phase; + } + + + *out++ = (float) (output / data->numSines); + } + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + int numStress; + paTestData data = {0}; + double load; + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d. MAX_LOAD = %f\n", + SAMPLE_RATE, FRAMES_PER_BUFFER, MAX_LOAD ); + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + err = Pa_OpenStream( + &stream, + paNoDevice,/* default input device */ + 0, /* no input */ + paFloat32, /* 32 bit floating point input */ + NULL, + Pa_GetDefaultOutputDeviceID(), /* default output device */ + 1, /* mono output */ + paFloat32, /* 32 bit floating point output */ + NULL, + SAMPLE_RATE, + FRAMES_PER_BUFFER, /* frames per buffer */ + 0, /* number of buffers, if zero then use default minimum */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + patestCallback, + &data ); + if( err != paNoError ) goto error; + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + /* Determine number of sines required to get to 50% */ + do + { + data.numSines++; + Pa_Sleep( 100 ); + + load = Pa_GetCPULoad( stream ); + printf("numSines = %d, CPU load = %f\n", data.numSines, load ); + } + while( load < 0.5 ); + + /* Calculate target stress value then ramp up to that level*/ + numStress = (int) (2.0 * data.numSines * MAX_LOAD ); + for( ; data.numSines < numStress; data.numSines++ ) + { + Pa_Sleep( 200 ); + load = Pa_GetCPULoad( stream ); + printf("STRESSING: numSines = %d, CPU load = %f\n", data.numSines, load ); + } + + printf("Suffer for 5 seconds.\n"); + Pa_Sleep( 5000 ); + + printf("Stop stream.\n"); + err = Pa_StopStream( stream ); + if( err != paNoError ) goto error; + + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + Pa_Terminate(); + printf("Test finished.\n"); + return err; +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + return err; +} diff --git a/pd/portaudio/pa_tests/patest_underflow.c b/pd/portaudio/pa_tests/patest_underflow.c new file mode 100644 index 00000000..3f02c5a4 --- /dev/null +++ b/pd/portaudio/pa_tests/patest_underflow.c @@ -0,0 +1,151 @@ +/* + * $Id: patest_underflow.c,v 1.1.1.1 2003-05-09 16:03:57 ggeiger Exp $ + * patest_underflow.c + * Simulate an output buffer underflow condition. + * Tests whether the stream can be stopped when underflowing buffers. + * + * Authors: + * Ross Bencina + * Phil Burk + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include "portaudio.h" + +#define NUM_SECONDS (20) +#define SAMPLE_RATE (44100) +#define FRAMES_PER_BUFFER (2048) +#define MSEC_PER_BUFFER ( (FRAMES_PER_BUFFER * 1000) / SAMPLE_RATE ) + +#ifndef M_PI +#define M_PI (3.14159265) +#endif + +#define TABLE_SIZE (200) +typedef struct +{ + float sine[TABLE_SIZE]; + int left_phase; + int right_phase; + int sleepTime; +} +paTestData; + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ +static int patestCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + paTestData *data = (paTestData*)userData; + float *out = (float*)outputBuffer; + unsigned long i; + int finished = 0; + (void) outTime; /* Prevent unused variable warnings. */ + (void) inputBuffer; + for( i=0; isine[data->left_phase]; /* left */ + *out++ = data->sine[data->right_phase]; /* right */ + data->left_phase += 1; + if( data->left_phase >= TABLE_SIZE ) data->left_phase -= TABLE_SIZE; + data->right_phase += 3; /* higher pitch so we can distinguish left and right. */ + if( data->right_phase >= TABLE_SIZE ) data->right_phase -= TABLE_SIZE; + } + + /* Cause underflow to occur. */ + if( data->sleepTime > 0 ) Pa_Sleep( data->sleepTime ); + data->sleepTime += 1; + + return finished; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PortAudioStream *stream; + PaError err; + paTestData data; + int i; + printf("PortAudio Test: output sine wave. SR = %d, BufSize = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER); + /* initialise sinusoidal wavetable */ + for( i=0; i +#include +#include "portaudio.h" + +#define SAMPLE_RATE (44100) + +typedef struct WireConfig_s +{ + int isInputInterleaved; + int isOutputInterleaved; + int numInputChannels; + int numOutputChannels; + int framesPerCallback; +} WireConfig_t; + +#define USE_FLOAT_INPUT (1) +#define USE_FLOAT_OUTPUT (1) + +#define INPUT_LATENCY_MSEC (0) +#define INPUT_LATENCY_FRAMES ((INPUT_LATENCY_MSEC * SAMPLE_RATE) / 1000) +#define OUTPUT_LATENCY_MSEC (0) +#define OUTPUT_LATENCY_FRAMES ((OUTPUT_LATENCY_MSEC * SAMPLE_RATE) / 1000) + + +#if USE_FLOAT_INPUT + #define INPUT_FORMAT paFloat32 + typedef float INPUT_SAMPLE; +#else + #define INPUT_FORMAT paInt16 + typedef short INPUT_SAMPLE; +#endif + +#if USE_FLOAT_OUTPUT + #define OUTPUT_FORMAT paFloat32 + typedef float OUTPUT_SAMPLE; +#else + #define OUTPUT_FORMAT paInt16 + typedef short OUTPUT_SAMPLE; +#endif + +double gInOutScaler = 1.0; +#define CONVERT_IN_TO_OUT(in) ((OUTPUT_SAMPLE) ((in) * gInOutScaler)) + +#define INPUT_DEVICE (Pa_GetDefaultInputDevice()) +#define OUTPUT_DEVICE (Pa_GetDefaultOutputDevice()) + +static PaError TestConfiguration( WireConfig_t *config ); + +static int wireCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); + +/* This routine will be called by the PortAudio engine when audio is needed. +** It may be called at interrupt level on some machines so don't do anything +** that could mess up the system like calling malloc() or free(). +*/ + +static int wireCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ) +{ + INPUT_SAMPLE *in; + OUTPUT_SAMPLE *out; + int inStride; + int outStride; + + int inDone = 0; + int outDone = 0; + WireConfig_t *config = (WireConfig_t *) userData; + unsigned int i; + int inChannel, outChannel; + (void) outTime; + + /* This may get called with NULL inputBuffer during initial setup. */ + if( inputBuffer == NULL) return 0; + + inChannel=0, outChannel=0; + while( !(inDone && outDone) ) + { + if( config->isInputInterleaved ) + { + in = ((INPUT_SAMPLE*)inputBuffer) + inChannel; + inStride = config->numInputChannels; + } + else + { + in = ((INPUT_SAMPLE**)inputBuffer)[inChannel]; + inStride = 1; + } + + if( config->isOutputInterleaved ) + { + out = ((OUTPUT_SAMPLE*)outputBuffer) + outChannel; + outStride = config->numOutputChannels; + } + else + { + out = ((OUTPUT_SAMPLE**)outputBuffer)[outChannel]; + outStride = 1; + } + + for( i=0; inumInputChannels - 1)) inChannel++; + else inDone = 1; + if(outChannel < (config->numOutputChannels - 1)) outChannel++; + else outDone = 1; + } + return 0; +} + +/*******************************************************************/ +int main(void); +int main(void) +{ + PaError err; + WireConfig_t CONFIG; + WireConfig_t *config = &CONFIG; + int configIndex = 0;; + + err = Pa_Initialize(); + if( err != paNoError ) goto error; + + printf("Please connect audio signal to input and listen for it on output!\n"); + printf("input format = %d\n", INPUT_FORMAT ); + printf("output format = %d\n", OUTPUT_FORMAT ); + printf("input device ID = %d\n", INPUT_DEVICE ); + printf("output device ID = %d\n", OUTPUT_DEVICE ); + + if( INPUT_FORMAT == OUTPUT_FORMAT ) + { + gInOutScaler = 1.0; + } + else if( (INPUT_FORMAT == paInt16) && (OUTPUT_FORMAT == paFloat32) ) + { + gInOutScaler = 1.0/32768.0; + } + else if( (INPUT_FORMAT == paFloat32) && (OUTPUT_FORMAT == paInt16) ) + { + gInOutScaler = 32768.0; + } + + for( config->isInputInterleaved = 0; config->isInputInterleaved < 2; config->isInputInterleaved++ ) + { + for( config->isOutputInterleaved = 0; config->isOutputInterleaved < 2; config->isOutputInterleaved++ ) + { + for( config->numInputChannels = 1; config->numInputChannels < 3; config->numInputChannels++ ) + { + for( config->numOutputChannels = 1; config->numOutputChannels < 3; config->numOutputChannels++ ) + { + for( config->framesPerCallback = 0; config->framesPerCallback < 65; config->framesPerCallback += 64 ) + { + printf("-----------------------------------------------\n" ); + printf("Configuration #%d\n", configIndex++ ); + err = TestConfiguration( config ); + // give user a chance to bail out + if( err == 1 ) + { + err = paNoError; + goto done; + } + else if( err != paNoError ) goto error; + } + } + } + } + } + +done: + Pa_Terminate(); + + printf("Full duplex sound test complete.\n"); fflush(stdout); + + printf("Hit ENTER to quit.\n"); fflush(stdout); + getchar(); + + return 0; + +error: + Pa_Terminate(); + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + printf("Hit ENTER to quit.\n"); fflush(stdout); + getchar(); + return -1; +} + +static PaError TestConfiguration( WireConfig_t *config ) +{ + int c; + PaError err; + PaStream *stream; + printf("input %sinterleaved!\n", (config->isInputInterleaved ? " " : "NOT ") ); + printf("output %sinterleaved!\n", (config->isOutputInterleaved ? " " : "NOT ") ); + printf("input channels = %d\n", config->numInputChannels ); + printf("output channels = %d\n", config->numOutputChannels ); + printf("framesPerCallback = %d\n", config->framesPerCallback ); + + err = Pa_OpenStream( + &stream, + INPUT_DEVICE, + config->numInputChannels, + INPUT_FORMAT | (config->isInputInterleaved ? 0 : paNonInterleaved), + INPUT_LATENCY_FRAMES, /* input latency */ + NULL, + OUTPUT_DEVICE, + config->numOutputChannels, + OUTPUT_FORMAT | (config->isOutputInterleaved ? 0 : paNonInterleaved), + OUTPUT_LATENCY_FRAMES, /* output latency */ + NULL, + SAMPLE_RATE, + config->framesPerCallback, /* frames per buffer */ + paClipOff, /* we won't output out of range samples so don't bother clipping them */ + wireCallback, + config ); /* user data */ + if( err != paNoError ) goto error; + + err = Pa_StartStream( stream ); + if( err != paNoError ) goto error; + + printf("Hit ENTER for next configuration, or 'q' to quit.\n"); fflush(stdout); + c = getchar(); + + printf("Closing stream.\n"); + err = Pa_CloseStream( stream ); + if( err != paNoError ) goto error; + + if( c == 'q' ) return 1; + +error: + return err; +} diff --git a/pd/portaudio/pa_unix/pa_unix_hostapis.c b/pd/portaudio/pa_unix/pa_unix_hostapis.c new file mode 100644 index 00000000..4d10ef93 --- /dev/null +++ b/pd/portaudio/pa_unix/pa_unix_hostapis.c @@ -0,0 +1,60 @@ +/* + * $Id: pa_unix_hostapis.c,v 1.1.2.4 2003/02/10 01:06:09 dmazzoni Exp $ + * Portable Audio I/O Library UNIX initialization table + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "pa_hostapi.h" + +PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + + +PaUtilHostApiInitializer *paHostApiInitializers[] = + { +#ifdef PA_USE_OSS + PaOSS_Initialize, +#endif + +#ifdef PA_USE_ALSA + PaAlsa_Initialize, +#endif + +#ifdef PA_USE_JACK + PaJack_Initialize, +#endif + + + 0 /* NULL terminated array */ + }; + +int paDefaultHostApiIndex = 0; + + diff --git a/pd/portaudio/pa_unix/pa_unix_hostapis.o b/pd/portaudio/pa_unix/pa_unix_hostapis.o new file mode 100644 index 00000000..e8d68536 Binary files /dev/null and b/pd/portaudio/pa_unix/pa_unix_hostapis.o differ diff --git a/pd/portaudio/pa_unix/pa_unix_util.c b/pd/portaudio/pa_unix/pa_unix_util.c new file mode 100644 index 00000000..826d4e7c --- /dev/null +++ b/pd/portaudio/pa_unix/pa_unix_util.c @@ -0,0 +1,102 @@ +/* + * $Id: pa_unix_util.c,v 1.1.2.1 2002/07/31 04:22:56 joshua Exp $ + * Portable Audio I/O Library + * UNIX platform-specific support functions + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2000 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. + */ + + +#include +#include + +#include "pa_util.h" + + +/* + Track memory allocations to avoid leaks. + */ + +#if PA_TRACK_MEMORY +static int numAllocations_ = 0; +#endif + + +void *PaUtil_AllocateMemory( long size ) +{ + void *result = malloc( size ); + +#if PA_TRACK_MEMORY + if( result != NULL ) numAllocations_ += 1; +#endif + return result; +} + + +void PaUtil_FreeMemory( void *block ) +{ + if( block != NULL ) + { + free( block ); +#if PA_TRACK_MEMORY + numAllocations_ -= 1; +#endif + + } +} + + +int PaUtil_CountCurrentlyAllocatedBlocks( void ) +{ +#if PA_TRACK_MEMORY + return numAllocations_; +#else + return 0; +#endif +} + + +void Pa_Sleep( long msec ) +{ + usleep( msec * 1000 ); +} + + +static int usePerformanceCounter_; +static double microsecondsPerTick_; + +void PaUtil_InitializeClock( void ) +{ + /* TODO */ +} + + +double PaUtil_GetTime( void ) +{ + /* TODO */ + return (0); +} diff --git a/pd/portaudio/pa_unix/pa_unix_util.o b/pd/portaudio/pa_unix/pa_unix_util.o new file mode 100644 index 00000000..52868e5f Binary files /dev/null and b/pd/portaudio/pa_unix/pa_unix_util.o differ diff --git a/pd/portaudio/pa_unix_oss/Makefile b/pd/portaudio/pa_unix_oss/Makefile new file mode 100644 index 00000000..f5015631 --- /dev/null +++ b/pd/portaudio/pa_unix_oss/Makefile @@ -0,0 +1,43 @@ +# Make PortAudio for Linux + +LIBS = -lm -lpthread + +CDEFINES = -I../pa_common +CFLAGS = -g -Wall +PASRC = ../pa_common/pa_lib.c pa_unix_oss.c +PAINC = ../pa_common/portaudio.h + +# Tests that work. +TESTC = $(PASRC) ../pa_tests/patest_sine.c +#TESTC = $(PASRC) ../pa_tests/patest_longsine.c +#TESTC = $(PASRC) ../pa_tests/patest_sine_time.c +#TESTC = $(PASRC) ../pa_tests/patest_maxsines.c +#TESTC = $(PASRC) ../pa_tests/patest_toomanysines.c +#TESTC = $(PASRC) ../pa_tests/patest_underflow.c +#TESTC = $(PASRC) ../pa_tests/patest_hang.c +#TESTC = $(PASRC) ../pa_tests/patest_sync.c +#TESTC = $(PASRC) ../pa_tests/patest_pink.c +#TESTC = $(PASRC) ../pa_tests/patest_leftright.c +#TESTC = $(PASRC) ../pa_tests/patest_clip.c +#TESTC = $(PASRC) ../pa_tests/patest_dither.c +#TESTC = $(PASRC) ../pa_tests/pa_devs.c +#TESTC = $(PASRC) ../pa_tests/patest_many.c +#TESTC = $(PASRC) ../pa_tests/patest_record.c +#TESTC = $(PASRC) ../pa_tests/pa_fuzz.c +#TESTC = $(PASRC) ../pa_tests/patest_wire.c +#TESTC = $(PASRC) ../pa_tests/paqa_devs.c + +# Tests that do not yet work. +# OSS doesn't let us make obscenely huge buffers so the test will seem to fail. But its OK. +#TESTC = $(PASRC) ../pa_tests/patest_stop.c + +TESTH = $(PAINC) + +all: patest + +patest: $(TESTC) $(TESTH) Makefile + gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest + +run: patest + ./patest + diff --git a/pd/portaudio/pa_unix_oss/Makefile_freebsd b/pd/portaudio/pa_unix_oss/Makefile_freebsd new file mode 100644 index 00000000..fc8b14db --- /dev/null +++ b/pd/portaudio/pa_unix_oss/Makefile_freebsd @@ -0,0 +1,36 @@ +# Make PortAudio for FreeBSD + +LIBS = -lm -pthread + +CDEFINES = -I../pa_common +CFLAGS = -g +PASRC = ../pa_common/pa_lib.c pa_freebsd.c +PAINC = ../pa_common/portaudio.h + +# Tests that work. +#TESTC = $(PASRC) ../pa_tests/patest_sine.c +TESTC = $(PASRC) ../pa_tests/patest_sine_time.c +#TESTC = $(PASRC) ../pa_tests/patest_stop.c +#TESTC = $(PASRC) ../pa_tests/patest_sync.c +#TESTC = $(PASRC) ../pa_tests/patest_pink.c +#TESTC = $(PASRC) ../pa_tests/patest_leftright.c +#TESTC = $(PASRC) ../pa_tests/patest_clip.c +#TESTC = $(PASRC) ../pa_tests/patest_dither.c +#TESTC = $(PASRC) ../pa_tests/pa_devs.c +#TESTC = $(PASRC) ../pa_tests/patest_many.c +#TESTC = $(PASRC) ../pa_tests/patest_record.c +#TESTC = $(PASRC) ../pa_tests/patest_wire.c +#TESTC = $(PASRC) ../pa_tests/paqa_devs.c + +# Tests that do not yet work. + +TESTH = $(PAINC) + +all: patest + +patest: $(TESTC) $(TESTH) Makefile + gcc $(CFLAGS) $(TESTC) $(CDEFINES) $(LIBS) -o patest + +run: patest + ./patest + diff --git a/pd/portaudio/pa_unix_oss/low_latency_tip.txt b/pd/portaudio/pa_unix_oss/low_latency_tip.txt new file mode 100644 index 00000000..2d982b79 Binary files /dev/null and b/pd/portaudio/pa_unix_oss/low_latency_tip.txt differ diff --git a/pd/portaudio/pa_unix_oss/pa_unix_oss.c b/pd/portaudio/pa_unix_oss/pa_unix_oss.c new file mode 100644 index 00000000..2e51b245 --- /dev/null +++ b/pd/portaudio/pa_unix_oss/pa_unix_oss.c @@ -0,0 +1,1187 @@ +/* + * $Id + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * OSS implementation by: + * Douglas Repetto + * Phil Burk + * Dominic Mazzoni + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +# include +# define DEVICE_NAME_BASE "/dev/dsp" +#else +# include /* JH20010905 */ +# define DEVICE_NAME_BASE "/dev/audio" +#endif + +#include "portaudio.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" + +/* TODO: add error text handling +#define PA_UNIX_OSS_ERROR( errorCode, errorText ) \ + PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText ) +*/ + +#define PRINT(x) { printf x; fflush(stdout); } +#define DBUG(x) /* PRINT(x) */ + +/* PaOSSHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + PaHostApiIndex hostApiIndex; +} +PaOSSHostApiRepresentation; + +typedef struct PaOSS_DeviceList { + PaDeviceInfo *deviceInfo; + struct PaOSS_DeviceList *next; +} +PaOSS_DeviceList; + +/* prototypes for functions declared in this file */ + +PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ); +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** s, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate, + unsigned long framesPerBuffer, + PaStreamFlags streamFlags, + PaStreamCallback *streamCallback, + void *userData ); +static PaError CloseStream( PaStream* stream ); +static PaError StartStream( PaStream *stream ); +static PaError StopStream( PaStream *stream ); +static PaError AbortStream( PaStream *stream ); +static PaError IsStreamStopped( PaStream *s ); +static PaError IsStreamActive( PaStream *stream ); +static PaTime GetStreamTime( PaStream *stream ); +static double GetStreamCpuLoad( PaStream* stream ); +static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); +static PaError WriteStream( PaStream* stream, void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); +static PaError BuildDeviceList( PaOSSHostApiRepresentation *hostApi ); + + +PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + PaOSSHostApiRepresentation *ossHostApi; + + DBUG(("PaOSS_Initialize\n")); + + ossHostApi = (PaOSSHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaOSSHostApiRepresentation) ); + if( !ossHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + ossHostApi->allocations = PaUtil_CreateAllocationGroup(); + if( !ossHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + *hostApi = &ossHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paOSS; + (*hostApi)->info.name = "OSS"; + ossHostApi->hostApiIndex = hostApiIndex; + + BuildDeviceList( ossHostApi ); + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &ossHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyReadWrite, PaUtil_DummyReadWrite, + PaUtil_DummyGetAvailable, PaUtil_DummyGetAvailable ); + + PaUtil_InitializeStreamInterface( &ossHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + return result; + +error: + if( ossHostApi ) + { + if( ossHostApi->allocations ) + { + PaUtil_FreeAllAllocations( ossHostApi->allocations ); + PaUtil_DestroyAllocationGroup( ossHostApi->allocations ); + } + + PaUtil_FreeMemory( ossHostApi ); + } + return result; +} + +#ifndef AFMT_S16_NE +#define AFMT_S16_NE Get_AFMT_S16_NE() +/********************************************************************* + * Some versions of OSS do not define AFMT_S16_NE. So check CPU. + * PowerPC is Big Endian. X86 is Little Endian. + */ +static int Get_AFMT_S16_NE( void ) +{ + long testData = 1; + char *ptr = (char *) &testData; + int isLittle = ( *ptr == 1 ); /* Does address point to least significant byte? */ + return isLittle ? AFMT_S16_LE : AFMT_S16_BE; +} +#endif + +PaError PaOSS_SetFormat(const char *callingFunctionName, int deviceHandle, + char *deviceName, int inputChannelCount, int outputChannelCount, + double *sampleRate) +{ + int format; + int rate; + int temp; + + /* Attempt to set format to 16-bit */ + + format = AFMT_S16_NE; + if (ioctl(deviceHandle, SNDCTL_DSP_SETFMT, &format) == -1) { + DBUG(("%s: could not set format: %s\n", callingFunctionName, deviceName )); + return paSampleFormatNotSupported; + } + if (format != AFMT_S16_NE) { + DBUG(("%s: device does not support AFMT_S16_NE: %s\n", callingFunctionName, deviceName )); + return paSampleFormatNotSupported; + } + + /* try to set the number of channels */ + + if (inputChannelCount > 0) { + temp = inputChannelCount; + + if( ioctl(deviceHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 ) { + DBUG(("%s: Couldn't set device %s to %d channels\n", callingFunctionName, deviceName, inputChannelCount )); + return paSampleFormatNotSupported; + } + } + + if (outputChannelCount > 0) { + temp = outputChannelCount; + + if( ioctl(deviceHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 ) { + DBUG(("%s: Couldn't set device %s to %d channels\n", callingFunctionName, deviceName, outputChannelCount )); + return paSampleFormatNotSupported; + } + } + + /* try to set the sample rate */ + + rate = (int)(*sampleRate); + if (ioctl(deviceHandle, SNDCTL_DSP_SPEED, &rate) == -1) + { + DBUG(("%s: Device %s, couldn't set sample rate to %d\n", + callingFunctionName, deviceName, (int)*sampleRate )); + return paInvalidSampleRate; + } + + /* reject if there's no sample rate within 1% of the one requested */ + if ((fabs(*sampleRate - rate) / *sampleRate) > 0.01) + { + DBUG(("%s: Device %s, wanted %d, closest sample rate was %d\n", + callingFunctionName, deviceName, (int)*sampleRate, rate )); + return paInvalidSampleRate; + } + + *sampleRate = rate; + + return paNoError; +} + +static PaError PaOSS_QueryDevice(char *deviceName, PaDeviceInfo *deviceInfo) +{ + PaError result = paNoError; + int tempDevHandle; + int numChannels, maxNumChannels; + int sampleRate; + int format; + + /* douglas: + we have to do this querying in a slightly different order. apparently + some sound cards will give you different info based on their settins. + e.g. a card might give you stereo at 22kHz but only mono at 44kHz. + the correct order for OSS is: format, channels, sample rate + */ + + if ( (tempDevHandle = open(deviceName,O_WRONLY|O_NONBLOCK)) == -1 ) + { + DBUG(("PaOSS_QueryDevice: could not open %s\n", deviceName )); + return paDeviceUnavailable; + } + + /* Attempt to set format to 16-bit */ + format = AFMT_S16_NE; + if (ioctl(tempDevHandle, SNDCTL_DSP_SETFMT, &format) == -1) { + DBUG(("PaOSS_QueryDevice: could not set format: %s\n", deviceName )); + result = paSampleFormatNotSupported; + goto error; + } + if (format != AFMT_S16_NE) { + DBUG(("PaOSS_QueryDevice: device does not support AFMT_S16_NE: %s\n", deviceName )); + result = paSampleFormatNotSupported; + goto error; + } + + /* Negotiate for the maximum number of channels for this device. PLB20010927 + * Consider up to 16 as the upper number of channels. + * Variable maxNumChannels should contain the actual upper limit after the call. + * Thanks to John Lazzaro and Heiko Purnhagen for suggestions. + */ + maxNumChannels = 0; + for( numChannels = 1; numChannels <= 16; numChannels++ ) + { + int temp = numChannels; + DBUG(("PaOSS_QueryDevice: use SNDCTL_DSP_CHANNELS, numChannels = %d\n", numChannels )) + if(ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp) < 0 ) + { + /* ioctl() failed so bail out if we already have stereo */ + if( numChannels > 2 ) break; + } + else + { + /* ioctl() worked but bail out if it does not support numChannels. + * We don't want to leave gaps in the numChannels supported. + */ + if( (numChannels > 2) && (temp != numChannels) ) break; + DBUG(("PaOSS_QueryDevice: temp = %d\n", temp )) + if( temp > maxNumChannels ) maxNumChannels = temp; /* Save maximum. */ + } + } + + /* The above negotiation may fail for an old driver so try this older technique. */ + if( maxNumChannels < 1 ) + { + int stereo = 1; + if(ioctl(tempDevHandle, SNDCTL_DSP_STEREO, &stereo) < 0) + { + maxNumChannels = 1; + } + else + { + maxNumChannels = (stereo) ? 2 : 1; + } + DBUG(("PaOSS_QueryDevice: use SNDCTL_DSP_STEREO, maxNumChannels = %d\n", maxNumChannels )) + } + + DBUG(("PaOSS_QueryDevice: maxNumChannels = %d\n", maxNumChannels)) + + deviceInfo->maxOutputChannels = maxNumChannels; + /* FIXME - for now, assume maxInputChannels = maxOutputChannels. + * Eventually do separate queries for O_WRONLY and O_RDONLY + */ + deviceInfo->maxInputChannels = deviceInfo->maxOutputChannels; + + /* During channel negotiation, the last ioctl() may have failed. This can + * also cause sample rate negotiation to fail. Hence the following, to return + * to a supported number of channels. SG20011005 */ + { + int temp = maxNumChannels; + if( temp > 2 ) temp = 2; /* use most reasonable default value */ + ioctl(tempDevHandle, SNDCTL_DSP_CHANNELS, &temp); + } + + /* Get supported sample rate closest to 44100 Hz */ + sampleRate = 44100; + if (ioctl(tempDevHandle, SNDCTL_DSP_SPEED, &sampleRate) == -1) + { + result = paUnanticipatedHostError; + goto error; + } + + deviceInfo->defaultSampleRate = sampleRate; + + deviceInfo->structVersion = 2; + + /* TODO */ + deviceInfo->defaultLowInputLatency = 128.0 / sampleRate; + deviceInfo->defaultLowOutputLatency = 128.0 / sampleRate; + deviceInfo->defaultHighInputLatency = 16384.0 / sampleRate; + deviceInfo->defaultHighOutputLatency = 16384.0 / sampleRate; + + result = paNoError; + +error: + /* We MUST close the handle here or we won't be able to reopen it later!!! */ + close(tempDevHandle); + + return result; +} + +static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi ) +{ + PaUtilHostApiRepresentation *commonApi = &ossApi->inheritedHostApiRep; + PaOSS_DeviceList *head = NULL, *tail = NULL, *entry; + int i; + int numDevices; + + /* Find devices by calling PaOSS_QueryDevice on each + potential device names. When we find a valid one, + add it to a linked list. */ + + for(i=0; i<10; i++) { + char deviceName[32]; + PaDeviceInfo deviceInfo; + int testResult; + + if (i==0) + sprintf(deviceName, "%s", DEVICE_NAME_BASE); + else + sprintf(deviceName, "%s%d", DEVICE_NAME_BASE, i); + + DBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); + testResult = PaOSS_QueryDevice(deviceName, &deviceInfo); + DBUG(("PaOSS BuildDeviceList: PaOSS_QueryDevice returned %d\n", testResult )); + + if (testResult == paNoError) { + DBUG(("PaOSS BuildDeviceList: Adding device %s to list\n", deviceName)); + deviceInfo.hostApi = ossApi->hostApiIndex; + deviceInfo.name = PaUtil_GroupAllocateMemory( + ossApi->allocations, strlen(deviceName)+1); + strcpy((char *)deviceInfo.name, deviceName); + entry = (PaOSS_DeviceList *)PaUtil_AllocateMemory(sizeof(PaOSS_DeviceList)); + entry->deviceInfo = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( + ossApi->allocations, sizeof(PaDeviceInfo) ); + entry->next = NULL; + memcpy(entry->deviceInfo, &deviceInfo, sizeof(PaDeviceInfo)); + if (tail) + tail->next = entry; + else { + head = entry; + tail = entry; + } + } + } + + /* Make an array of PaDeviceInfo pointers out of the linked list */ + + numDevices = 0; + entry = head; + while(entry) { + numDevices++; + entry = entry->next; + } + + DBUG(("PaOSS BuildDeviceList: Total number of devices found: %d\n", numDevices)); + + commonApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + ossApi->allocations, sizeof(PaDeviceInfo*) *numDevices ); + + entry = head; + i = 0; + while(entry) { + commonApi->deviceInfos[i] = entry->deviceInfo; + i++; + entry = entry->next; + } + + commonApi->info.deviceCount = numDevices; + commonApi->info.defaultInputDevice = 0; + commonApi->info.defaultOutputDevice = 0; + + return paNoError; +} + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi; + + if( ossHostApi->allocations ) + { + PaUtil_FreeAllAllocations( ossHostApi->allocations ); + PaUtil_DestroyAllocationGroup( ossHostApi->allocations ); + } + + PaUtil_FreeMemory( ossHostApi ); +} + +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + PaDeviceIndex device; + PaDeviceInfo *deviceInfo; + PaError result = paNoError; + char *deviceName; + int inputChannelCount, outputChannelCount; + int tempDevHandle = 0; + int flags; + PaSampleFormat inputSampleFormat, outputSampleFormat; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + } + + if (inputChannelCount == 0 && outputChannelCount == 0) + return paInvalidChannelCount; + + /* if full duplex, make sure that they're the same device */ + + if (inputChannelCount > 0 && outputChannelCount > 0 && + inputParameters->device != outputParameters->device) + return paInvalidDevice; + + /* if full duplex, also make sure that they're the same number of channels */ + + if (inputChannelCount > 0 && outputChannelCount > 0 && + inputChannelCount != outputChannelCount) + return paInvalidChannelCount; + + /* open the device so we can do more tests */ + + if (inputChannelCount > 0) { + result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi); + if (result != paNoError) + return result; + } + else { + result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi); + if (result != paNoError) + return result; + } + + deviceInfo = hostApi->deviceInfos[device]; + deviceName = (char *)deviceInfo->name; + + flags = O_NONBLOCK; + if (inputChannelCount > 0 && outputChannelCount > 0) + flags |= O_RDWR; + else if (inputChannelCount > 0) + flags |= O_RDONLY; + else + flags |= O_WRONLY; + + if ( (tempDevHandle = open(deviceInfo->name, flags)) == -1 ) + { + DBUG(("PaOSS IsFormatSupported: could not open %s\n", deviceName )); + return paDeviceUnavailable; + } + + /* PaOSS_SetFormat will do the rest of the checking for us */ + + if ((result = PaOSS_SetFormat("PaOSS IsFormatSupported", tempDevHandle, + deviceName, inputChannelCount, outputChannelCount, + &sampleRate)) != paNoError) + { + goto error; + } + + /* everything succeeded! */ + + close(tempDevHandle); + + return paFormatIsSupported; + + error: + if (tempDevHandle) + close(tempDevHandle); + + return paSampleFormatNotSupported; +} + +/* PaOSSStream - a stream data structure specifically for this implementation */ + +typedef struct PaOSSStream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + + int deviceHandle; + + int stopSoon; + int stopNow; + int isActive; + + int inputChannelCount; + int outputChannelCount; + + pthread_t thread; + + void *inputBuffer; + void *outputBuffer; + + int lastPosPtr; + double lastStreamBytes; + + int framesProcessed; + + double sampleRate; + + unsigned long framesPerHostCallback; +} +PaOSSStream; + +/* 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; + PaOSSHostApiRepresentation *ossHostApi = (PaOSSHostApiRepresentation*)hostApi; + PaOSSStream *stream = 0; + PaDeviceIndex device; + PaDeviceInfo *deviceInfo; + audio_buf_info bufinfo; + int bytesPerHostBuffer; + int flags; + int deviceHandle = 0; + char *deviceName; + unsigned long framesPerHostBuffer; + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat = paInt16, outputSampleFormat = paInt16; + PaSampleFormat hostInputSampleFormat = paInt16, hostOutputSampleFormat = paInt16; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16, inputSampleFormat ); + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16, outputSampleFormat ); + } + else + { + outputChannelCount = 0; + } + + if( inputChannelCount == 0 && outputChannelCount == 0 ) + { + DBUG(("Both inputChannelCount and outputChannelCount are zero!\n")); + return paUnanticipatedHostError; + } + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + /* + * open the device and set parameters here + */ + + if (inputChannelCount == 0 && outputChannelCount == 0) + return paInvalidChannelCount; + + /* if full duplex, make sure that they're the same device */ + + if (inputChannelCount > 0 && outputChannelCount > 0 && + inputParameters->device != outputParameters->device) + return paInvalidDevice; + + /* if full duplex, also make sure that they're the same number of channels */ + + if (inputChannelCount > 0 && outputChannelCount > 0 && + inputChannelCount != outputChannelCount) + return paInvalidChannelCount; + + if (inputChannelCount > 0) { + result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, inputParameters->device, hostApi); + if (result != paNoError) + return result; + } + else { + result = PaUtil_DeviceIndexToHostApiDeviceIndex(&device, outputParameters->device, hostApi); + if (result != paNoError) + return result; + } + + deviceInfo = hostApi->deviceInfos[device]; + deviceName = (char *)deviceInfo->name; + + flags = O_NONBLOCK; + if (inputChannelCount > 0 && outputChannelCount > 0) + flags |= O_RDWR; + else if (inputChannelCount > 0) + flags |= O_RDONLY; + else + flags |= O_WRONLY; + + /* open first in nonblocking mode, in case it's busy... */ + if ( (deviceHandle = open(deviceInfo->name, flags)) == -1 ) + { + DBUG(("PaOSS OpenStream: could not open %s\n", deviceName )); + return paDeviceUnavailable; + } + + /* if that succeeded, immediately open it again in blocking mode */ + close(deviceHandle); + flags -= O_NONBLOCK; + if ( (deviceHandle = open(deviceInfo->name, flags)) == -1 ) + { + DBUG(("PaOSS OpenStream: could not open %s in blocking mode\n", deviceName )); + return paDeviceUnavailable; + } + + if ((result = PaOSS_SetFormat("PaOSS OpenStream", deviceHandle, + deviceName, inputChannelCount, outputChannelCount, + &sampleRate)) != paNoError) + { + goto error; + } + + /* Compute number of frames per host buffer - if we can't retrieve the + * value, use the user's value instead + */ + + if ( ioctl(deviceHandle, SNDCTL_DSP_GETBLKSIZE, &bytesPerHostBuffer) == 0) + { + framesPerHostBuffer = bytesPerHostBuffer / 2 / (inputChannelCount>0? inputChannelCount: outputChannelCount); + } + else + framesPerHostBuffer = framesPerBuffer; + + /* Allocate stream and fill in structure */ + + stream = (PaOSSStream*)PaUtil_AllocateMemory( sizeof(PaOSSStream) ); + if( !stream ) + { + result = paInsufficientMemory; + goto error; + } + + if( streamCallback ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &ossHostApi->callbackStreamInterface, streamCallback, userData ); + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &ossHostApi->blockingStreamInterface, streamCallback, userData ); + } + + stream->streamRepresentation.streamInfo.inputLatency = 0.; + stream->streamRepresentation.streamInfo.outputLatency = 0.; + + if (inputChannelCount > 0) { + if (ioctl( deviceHandle, SNDCTL_DSP_GETISPACE, &bufinfo) == 0) + stream->streamRepresentation.streamInfo.inputLatency = + (bufinfo.fragsize * bufinfo.fragstotal) / sampleRate; + } + + if (outputChannelCount > 0) { + if (ioctl( deviceHandle, SNDCTL_DSP_GETOSPACE, &bufinfo) == 0) + stream->streamRepresentation.streamInfo.outputLatency = + (bufinfo.fragsize * bufinfo.fragstotal) / sampleRate; + } + + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + /* we assume a fixed host buffer size in this example, but the buffer processor + can also support bounded and unknown host buffer sizes by passing + paUtilBoundedHostBufferSize or paUtilUnknownHostBufferSize instead of + paUtilFixedHostBufferSize below. */ + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, hostInputSampleFormat, + outputChannelCount, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, + framesPerHostBuffer, paUtilFixedHostBufferSize, + streamCallback, userData ); + if( result != paNoError ) + goto error; + + stream->framesPerHostCallback = framesPerHostBuffer; + + stream->stopSoon = 0; + stream->stopNow = 0; + stream->isActive = 0; + stream->thread = 0; + stream->lastPosPtr = 0; + stream->lastStreamBytes = 0; + stream->sampleRate = sampleRate; + stream->framesProcessed = 0; + stream->deviceHandle = deviceHandle; + + if (inputChannelCount > 0) + stream->inputBuffer = PaUtil_AllocateMemory( 2 * framesPerHostBuffer * inputChannelCount ); + else + stream->inputBuffer = NULL; + + if (outputChannelCount > 0) + stream->outputBuffer = PaUtil_AllocateMemory( 2 * framesPerHostBuffer * outputChannelCount ); + else + stream->outputBuffer = NULL; + + stream->inputChannelCount = inputChannelCount; + stream->outputChannelCount = outputChannelCount; + + *s = (PaStream*)stream; + + result = paNoError; + + return result; + +error: + if( stream ) + PaUtil_FreeMemory( stream ); + + if( deviceHandle ) + close( deviceHandle ); + + return result; +} + +static void *PaOSS_AudioThreadProc(void *userData) +{ + PaOSSStream *stream = (PaOSSStream*)userData; + + DBUG(("PaOSS AudioThread: %d in, %d out\n", stream->inputChannelCount, stream->outputChannelCount)); + + while( (stream->stopNow == 0) && (stream->stopSoon == 0) ) { + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* TODO: IMPLEMENT ME */ + int callbackResult; + unsigned long framesProcessed; + int bytesRequested; + int bytesRead, bytesWritten; + int delta; + int result; + count_info info; + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo ); + + /* + depending on whether the host buffers are interleaved, non-interleaved + or a mixture, you will want to call PaUtil_SetInterleaved*Channels(), + PaUtil_SetNonInterleaved*Channel() or PaUtil_Set*Channel() here. + */ + + if ( stream->inputChannelCount > 0 ) + { + bytesRequested = stream->framesPerHostCallback * 2 * stream->inputChannelCount; + bytesRead = read( stream->deviceHandle, stream->inputBuffer, bytesRequested ); + + PaUtil_SetInputFrameCount( &stream->bufferProcessor, bytesRead/(2*stream->inputChannelCount)); + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, + 0, /* first channel of inputBuffer is channel 0 */ + stream->inputBuffer, + 0 ); /* 0 - use inputChannelCount passed to init buffer processor */ + } + + if ( stream->outputChannelCount > 0 ) + { + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, + 0, /* first channel of outputBuffer is channel 0 */ + stream->outputBuffer, + 0 ); /* 0 - use outputChannelCount passed to init buffer processor */ + } + + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + + if( callbackResult == paContinue ) + { + /* nothing special to do */ + } + else if( callbackResult == paAbort ) + { + /* once finished, call the finished callback */ + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + + return NULL; /* return from the loop */ + } + else if ( callbackResult == paComplete ) + { + /* User callback has asked us to stop with paComplete or other non-zero value */ + + /* once finished, call the finished callback */ + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + + stream->stopSoon = 1; + } + + if ( stream->outputChannelCount > 0 ) { + /* write output samples AFTER we've checked the callback result code */ + + bytesRequested = stream->framesPerHostCallback * 2 * stream->outputChannelCount; + bytesWritten = write( stream->deviceHandle, stream->outputBuffer, bytesRequested ); + + /* TODO: handle bytesWritten != bytesRequested (slippage?) */ + } + + /* Update current stream time (using a double so that + we don't wrap around like info.bytes does) */ + if( stream->outputChannelCount > 0 ) + result = ioctl( stream->deviceHandle, SNDCTL_DSP_GETOPTR, &info); + else + result = ioctl( stream->deviceHandle, SNDCTL_DSP_GETIPTR, &info); + + if (result == 0) { + delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF; + stream->lastStreamBytes += delta; + stream->lastPosPtr = info.bytes; + } + + stream->framesProcessed += stream->framesPerHostCallback; + } + + return NULL; +} + +/* + 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; + PaOSSStream *stream = (PaOSSStream*)s; + + close(stream->deviceHandle); + + if ( stream->inputBuffer ) + PaUtil_FreeMemory( stream->inputBuffer ); + if ( stream->outputBuffer ) + PaUtil_FreeMemory( stream->outputBuffer ); + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + + return result; +} + + +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaOSSStream *stream = (PaOSSStream*)s; + int presult; + + stream->isActive = 1; + stream->lastPosPtr = 0; + stream->lastStreamBytes = 0; + stream->framesProcessed = 0; + + DBUG(("PaOSS StartStream\n")); + + presult = pthread_create(&stream->thread, + NULL /*pthread_attr_t * attr*/, + (void*)PaOSS_AudioThreadProc, (void *)stream); + + return result; +} + + +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaOSSStream *stream = (PaOSSStream*)s; + + stream->stopSoon = 1; + pthread_join( stream->thread, NULL ); + stream->stopSoon = 0; + stream->stopNow = 0; + stream->isActive = 0; + + DBUG(("PaOSS StopStream: Stopped stream\n")); + + return result; +} + + +static PaError AbortStream( PaStream *s ) +{ + PaError result = paNoError; + PaOSSStream *stream = (PaOSSStream*)s; + + stream->stopNow = 1; + pthread_join( stream->thread, NULL ); + stream->stopSoon = 0; + stream->stopNow = 0; + stream->isActive = 0; + + DBUG(("PaOSS AbortStream: Stopped stream\n")); + + return result; +} + + +static PaError IsStreamStopped( PaStream *s ) +{ + PaOSSStream *stream = (PaOSSStream*)s; + + return (!stream->isActive); +} + + +static PaError IsStreamActive( PaStream *s ) +{ + PaOSSStream *stream = (PaOSSStream*)s; + + return (stream->isActive); +} + + +static PaTime GetStreamTime( PaStream *s ) +{ + PaOSSStream *stream = (PaOSSStream*)s; + count_info info; + int delta; + + if( stream->outputChannelCount > 0 ) { + if (ioctl( stream->deviceHandle, SNDCTL_DSP_GETOPTR, &info) == 0) { + delta = ( info.bytes - stream->lastPosPtr ) & 0x000FFFFF; + return ( stream->lastStreamBytes + delta) / ( stream->outputChannelCount * 2 ) / stream->sampleRate; + } + } + else { + if (ioctl( stream->deviceHandle, SNDCTL_DSP_GETIPTR, &info) == 0) { + delta = (info.bytes - stream->lastPosPtr) & 0x000FFFFF; + return ( stream->lastStreamBytes + delta) / ( stream->inputChannelCount * 2 ) / stream->sampleRate; + } + } + + /* the ioctl failed, but we can still give a coarse estimate */ + + return stream->framesProcessed / stream->sampleRate; +} + + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaOSSStream *stream = (PaOSSStream*)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 ) +{ + PaOSSStream *stream = (PaOSSStream*)s; + int bytesRequested, bytesRead; + + bytesRequested = frames * 2 * stream->inputChannelCount; + bytesRead = read( stream->deviceHandle, stream->inputBuffer, bytesRequested ); + + if ( bytesRequested != bytesRead ) + return paUnanticipatedHostError; + else + return paNoError; +} + + +static PaError WriteStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaOSSStream *stream = (PaOSSStream*)s; + int bytesRequested, bytesWritten; + + bytesRequested = frames * 2 * stream->outputChannelCount; + bytesWritten = write( stream->deviceHandle, buffer, bytesRequested ); + + if ( bytesRequested != bytesWritten ) + return paUnanticipatedHostError; + else + return paNoError; +} + + +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaOSSStream *stream = (PaOSSStream*)s; + audio_buf_info info; + + if ( ioctl(stream->deviceHandle, SNDCTL_DSP_GETISPACE, &info) == 0) + { + int bytesAvailable = info.fragments * info.fragsize; + return ( bytesAvailable / 2 / stream->inputChannelCount ); + } + else + return 0; /* TODO: is this right for "don't know"? */ +} + + +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaOSSStream *stream = (PaOSSStream*)s; + + audio_buf_info info; + + if ( ioctl(stream->deviceHandle, SNDCTL_DSP_GETOSPACE, &info) == 0) + { + int bytesAvailable = info.fragments * info.fragsize; + return ( bytesAvailable / 2 / stream->outputChannelCount ); + } + else + return 0; /* TODO: is this right for "don't know"? */ +} + diff --git a/pd/portaudio/pa_unix_oss/pa_unix_oss.o b/pd/portaudio/pa_unix_oss/pa_unix_oss.o new file mode 100644 index 00000000..604f9798 Binary files /dev/null and b/pd/portaudio/pa_unix_oss/pa_unix_oss.o differ diff --git a/pd/portaudio/pa_unix_oss/recplay.c b/pd/portaudio/pa_unix_oss/recplay.c new file mode 100644 index 00000000..9d4c78cf --- /dev/null +++ b/pd/portaudio/pa_unix_oss/recplay.c @@ -0,0 +1,114 @@ +/* + * recplay.c + * Phil Burk + * Minimal record and playback test. + * + */ +#include +#include +#include +#ifndef __STDC__ +/* #include */ +#endif /* __STDC__ */ +#include +#ifdef __STDC__ +#include +#else /* __STDC__ */ +#include +#endif /* __STDC__ */ +#include + +#define NUM_BYTES (64*1024) +#define BLOCK_SIZE (4*1024) + +#define AUDIO "/dev/dsp" + +char buffer[NUM_BYTES]; + +int audioDev = 0; + +main (int argc, char *argv[]) +{ + int numLeft; + char *ptr; + int num; + int samplesize; + + /********** RECORD ********************/ + /* Open audio device. */ + audioDev = open (AUDIO, O_RDONLY, 0); + if (audioDev == -1) + { + perror (AUDIO); + exit (-1); + } + + /* Set to 16 bit samples. */ + samplesize = 16; + ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize); + if (samplesize != 16) + { + perror("Unable to set the sample size."); + exit(-1); + } + + /* Record in blocks */ + printf("Begin recording.\n"); + numLeft = NUM_BYTES; + ptr = buffer; + while( numLeft >= BLOCK_SIZE ) + { + if ( (num = read (audioDev, ptr, BLOCK_SIZE)) < 0 ) + { + perror (AUDIO); + exit (-1); + } + else + { + printf("Read %d bytes\n", num); + ptr += num; + numLeft -= num; + } + } + + close( audioDev ); + + /********** PLAYBACK ********************/ + /* Open audio device for writing. */ + audioDev = open (AUDIO, O_WRONLY, 0); + if (audioDev == -1) + { + perror (AUDIO); + exit (-1); + } + + /* Set to 16 bit samples. */ + samplesize = 16; + ioctl(audioDev, SNDCTL_DSP_SAMPLESIZE, &samplesize); + if (samplesize != 16) + { + perror("Unable to set the sample size."); + exit(-1); + } + + /* Play in blocks */ + printf("Begin playing.\n"); + numLeft = NUM_BYTES; + ptr = buffer; + while( numLeft >= BLOCK_SIZE ) + { + if ( (num = write (audioDev, ptr, BLOCK_SIZE)) < 0 ) + { + perror (AUDIO); + exit (-1); + } + else + { + printf("Wrote %d bytes\n", num); + ptr += num; + numLeft -= num; + } + } + + close( audioDev ); +} diff --git a/pd/portaudio/pa_win/pa_win_hostapis.c b/pd/portaudio/pa_win/pa_win_hostapis.c new file mode 100644 index 00000000..2b518512 --- /dev/null +++ b/pd/portaudio/pa_win/pa_win_hostapis.c @@ -0,0 +1,63 @@ +/* + * $Id: pa_win_hostapis.c,v 1.1.2.6 2002/10/26 05:32:35 rossbencina Exp $ + * Portable Audio I/O Library Windows initialization table + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2002 Ross Bencina, Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "pa_hostapi.h" + +PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); + + +PaUtilHostApiInitializer *paHostApiInitializers[] = + { + +#ifndef PA_NO_WMME + PaWinMme_Initialize, +#endif + +#ifndef PA_NO_DS + PaWinDs_Initialize, +#endif + +#ifndef PA_NO_ASIO + PaAsio_Initialize, +#endif + + PaSkeleton_Initialize, /* just for testing */ + + 0 /* NULL terminated array */ + }; + + +int paDefaultHostApiIndex = 0; + diff --git a/pd/portaudio/pa_win/pa_win_util.c b/pd/portaudio/pa_win/pa_win_util.c new file mode 100644 index 00000000..f92d1e35 --- /dev/null +++ b/pd/portaudio/pa_win/pa_win_util.c @@ -0,0 +1,128 @@ +/* + * $Id: pa_win_util.c,v 1.1.2.5 2002/10/18 18:36:57 rossbencina Exp $ + * Portable Audio I/O Library + * Win32 platform-specific support functions + * + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 1999-2000 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. + */ + + +#include +#include /* for timeGetTime() */ + +#include "pa_util.h" + + +/* + Track memory allocations to avoid leaks. + */ + +#if PA_TRACK_MEMORY +static int numAllocations_ = 0; +#endif + + +void *PaUtil_AllocateMemory( long size ) +{ + void *result = GlobalAlloc( GPTR, size ); + +#if PA_TRACK_MEMORY + if( result != NULL ) numAllocations_ += 1; +#endif + return result; +} + + +void PaUtil_FreeMemory( void *block ) +{ + if( block != NULL ) + { + GlobalFree( block ); +#if PA_TRACK_MEMORY + numAllocations_ -= 1; +#endif + + } +} + + +int PaUtil_CountCurrentlyAllocatedBlocks( void ) +{ +#if PA_TRACK_MEMORY + return numAllocations_; +#else + return 0; +#endif +} + + +void Pa_Sleep( long msec ) +{ + Sleep( msec ); +} + +static int usePerformanceCounter_; +static double secondsPerTick_; + +void PaUtil_InitializeClock( void ) +{ + LARGE_INTEGER ticksPerSecond; + + if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 ) + { + usePerformanceCounter_ = 1; + secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart; + } + else + { + usePerformanceCounter_ = 0; + } +} + + +double PaUtil_GetTime( void ) +{ + LARGE_INTEGER time; + + if( usePerformanceCounter_ ) + { + /* FIXME: + according to this knowledge-base article, QueryPerformanceCounter + can skip forward by seconds! + http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323& + + it may be better to use the rtdsc instruction using inline asm, + however then a method is needed to calculate a ticks/seconds ratio. + */ + QueryPerformanceCounter( &time ); + return time.QuadPart * secondsPerTick_; + } + else + { + return timeGetTime() * .001; + } +} diff --git a/pd/portaudio/pa_win/pa_x86_plain_converters.c b/pd/portaudio/pa_win/pa_x86_plain_converters.c new file mode 100644 index 00000000..98442a8c --- /dev/null +++ b/pd/portaudio/pa_win/pa_x86_plain_converters.c @@ -0,0 +1,1167 @@ +#include "pa_x86_plain_converters.h" + +#include "pa_converters.h" +#include "pa_dither.h" + +/* + plain intel assemby versions of standard pa converter functions. + + the main reason these versions are faster than the equivalent C versions + is that float -> int casting is expensive in C on x86 because the rounding + mode needs to be changed for every cast. these versions only set + the rounding mode once outside the loop. + + small additional speed gains are made by the way that clamping is + implemented. + +TODO: + o- inline dither code + o- implement Dither only (no-clip) versions + o- implement int8 and uint8 versions + o- test thouroughly + + o- the packed 24 bit functions could benefit from unrolling and avoiding + byte and word sized register access. +*/ + +/* -------------------------------------------------------------------------- */ + +/* +#define PA_CLIP_( val, min, max )\ + { val = ((val) < (min)) ? (min) : (((val) > (max)) ? (max) : (val)); } +*/ + +/* + the following notes were used to determine whether a floating point + value should be saturated (ie >1 or <-1) by loading it into an integer + register. these should be rewritten so that they make sense. + + an ieee floating point value + + 1.xxxxxxxxxxxxxxxxxxxx? + + + is less than or equal to 1 and greater than or equal to -1 either: + + if the mantissa is 0 and the unbiased exponent is 0 + + OR + + if the unbiased exponent < 0 + + this translates to: + + if the mantissa is 0 and the biased exponent is 7F + + or + + if the biased exponent is less than 7F + + + therefore the value is greater than 1 or less than -1 if + + the mantissa is not 0 and the biased exponent is 7F + + or + + if the biased exponent is greater than 7F + + + in other words, if we mask out the sign bit, the value is + greater than 1 or less than -1 if its integer representation is greater than: + + 0 01111111 0000 0000 0000 0000 0000 000 + + 0011 1111 1000 0000 0000 0000 0000 0000 => 0x3F800000 +*/ + +/* -------------------------------------------------------------------------- */ + +static const short fpuControlWord_ = 0x033F; /*round to nearest, 64 bit precision, all exceptions masked*/ +static const double int32Scaler_ = 0x7FFFFFFF; +static const double ditheredInt32Scaler_ = 0x7FFFFFFE; +static const double int24Scaler_ = 0x7FFFFF; +static const double ditheredInt24Scaler_ = 0x7FFFFE; +static const double int16Scaler_ = 0x7FFF; +static const double ditheredInt16Scaler_ = 0x7FFE; + +#define PA_DITHER_BITS_ (15) +/* Multiply by PA_FLOAT_DITHER_SCALE_ to get a float between -2.0 and +1.99999 */ +#define PA_FLOAT_DITHER_SCALE_ (1.0 / ((1< source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int32Scaler_ // stack: (int)0x7FFFFFFF + + Float32_To_Int32_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF + /* + note: we could store to a temporary qword here which would cause + wraparound distortion instead of int indefinite 0x10. that would + be more work, and given that not enabling clipping is only advisable + when you know that your signal isn't going to clip it isn't worth it. + */ + fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + (void) ditherGenerator; // unused parameter + + while( count-- ) + { + // REVIEW + double scaled = *src * 0x7FFFFFFF; + PA_CLIP_( scaled, -2147483648., 2147483647. ); + *dest = (signed long) scaled; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int32Scaler_ // stack: (int)0x7FFFFFFF + + Float32_To_Int32_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int32_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFFFF, (int)0x7FFFFFFF + fistp dword ptr [edi] // pop st(0) into dest, stack: (int)0x7FFFFFFF + jmp Float32_To_Int32_Clip_stored + + Float32_To_Int32_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFFFF // convert to maximum range integers + mov dword ptr [edi], edx + + Float32_To_Int32_Clip_stored: + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int32_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ + /* + float *src = (float*)sourceBuffer; + signed long *dest = (signed long*)destinationBuffer; + + while( count-- ) + { + // REVIEW + 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; + + + src += sourceStride; + dest += destinationStride; + } + */ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 and int32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt32Scaler_ // stack: int scaler + + Float32_To_Int32_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int32_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither + value*(int scaler), int scaler + fistp dword ptr [edi] // pop st(0) into dest, stack: int scaler + jmp Float32_To_Int32_DitherClip_stored + + Float32_To_Int32_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFFFF // convert to maximum range integers + mov dword ptr [edi], edx + + Float32_To_Int32_DitherClip_stored: + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int32_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, 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; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + signed long tempInt32; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int24Scaler_ // stack: (int)0x7FFFFF + + Float32_To_Int24_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF + fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF + mov edx, tempInt32 + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, 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; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + signed long tempInt32; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int24Scaler_ // stack: (int)0x7FFFFF + + Float32_To_Int24_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int24_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFFFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFFFF, (int)0x7FFFFF + fistp tempInt32 // pop st(0) into tempInt32, stack: (int)0x7FFFFF + mov edx, tempInt32 + jmp Float32_To_Int24_Clip_store + + Float32_To_Int24_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFF // convert to maximum range integers + + Float32_To_Int24_Clip_store: + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int24_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, 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 + + // FIXME: the dither amplitude here appears to be too small by 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; + + dest[0] = (unsigned char)(temp >> 8); + dest[1] = (unsigned char)(temp >> 16); + dest[2] = (unsigned char)(temp >> 24); + + src += sourceStride; + dest += destinationStride * 3; + } +*/ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + signed long tempInt32; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx + + mov ecx, count + imul ecx, eax + add ecx, esi + + mov edi, destinationBuffer + + mov edx, 3 // sizeof int24 + mov ebx, destinationStride + imul ebx, edx + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt24Scaler_ // stack: int scaler + + Float32_To_Int24_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int24_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler + fistp tempInt32 // pop st(0) into tempInt32, stack: int scaler + mov edx, tempInt32 + jmp Float32_To_Int24_DitherClip_store + + Float32_To_Int24_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add edx, 0x7FFFFF // convert to maximum range integers + + Float32_To_Int24_DitherClip_store: + + mov byte ptr [edi], DL + shr edx, 8 + //mov byte ptr [edi+1], DL + //mov byte ptr [edi+2], DH + mov word ptr [edi+1], DX + + //add edi, ebx // increment destination ptr + lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int24_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + + short samp = (short) (*src * (32767.0f)); + *dest = samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int16Scaler_ // stack: (int)0x7FFF + + Float32_To_Int16_loop: + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF + fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_Clip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, PaUtilTriangularDitherGenerator *ditherGenerator ) +{ +/* + float *src = (float*)sourceBuffer; + signed short *dest = (signed short*)destinationBuffer; + (void)ditherGenerator; // unused parameter + + while( count-- ) + { + long samp = (signed long) (*src * (32767.0f)); + PA_CLIP_( samp, -0x8000, 0x7FFF ); + *dest = (signed short) samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + (void) ditherGenerator; /* unused parameter */ + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld int16Scaler_ // stack: (int)0x7FFF + + Float32_To_Int16_Clip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int16_Clip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, (int)0x7FFF + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*0x7FFF, (int)0x7FFF + fistp word ptr [edi] // store scaled int into dest, stack: (int)0x7FFF + jmp Float32_To_Int16_Clip_stored + + Float32_To_Int16_Clip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add dx, 0x7FFF // convert to maximum range integers + mov word ptr [edi], dx // store clamped into into dest + + Float32_To_Int16_Clip_stored: + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_Clip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } +} + +/* -------------------------------------------------------------------------- */ + +static void Float32_To_Int16_DitherClip( + void *destinationBuffer, signed int destinationStride, + void *sourceBuffer, signed int sourceStride, + unsigned int count, 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 ); + *dest = (signed short) samp; + + src += sourceStride; + dest += destinationStride; + } +*/ + + short savedFpuControlWord; + + // spill storage: + signed long sourceByteStride; + signed long highpassedDither; + + // dither state: + unsigned long ditherPrevious = ditherGenerator->previous; + unsigned long ditherRandSeed1 = ditherGenerator->randSeed1; + unsigned long ditherRandSeed2 = ditherGenerator->randSeed2; + + __asm{ + // esi -> source ptr + // eax -> source byte stride + // edi -> destination ptr + // ebx -> destination byte stride + // ecx -> source end ptr + // edx -> temp + + mov esi, sourceBuffer + + mov edx, 4 // sizeof float32 + mov eax, sourceStride + imul eax, edx // source byte stride + + mov ecx, count + imul ecx, eax + add ecx, esi // source end ptr = count * source byte stride + source ptr + + mov edi, destinationBuffer + + mov edx, 2 // sizeof int16 + mov ebx, destinationStride + imul ebx, edx // destination byte stride + + fwait + fstcw savedFpuControlWord + fldcw fpuControlWord_ + + fld ditheredInt16Scaler_ // stack: int scaler + + Float32_To_Int16_DitherClip_loop: + + mov edx, dword ptr [esi] // load floating point value into integer register + + and edx, 0x7FFFFFFF // mask off sign + cmp edx, 0x3F800000 // greater than 1.0 or less than -1.0 + + jg Float32_To_Int16_DitherClip_clamp + + // load unscaled value into st(0) + fld dword ptr [esi] // stack: value, int scaler + add esi, eax // increment source ptr + //lea esi, [esi+eax] + fmul st(0), st(1) // st(0) *= st(1), stack: value*(int scaler), int scaler + + /* + // call PaUtil_GenerateFloatTriangularDither with C calling convention + mov sourceByteStride, eax // save eax + mov sourceEnd, ecx // save ecx + push ditherGenerator // pass ditherGenerator parameter on stack + call PaUtil_GenerateFloatTriangularDither // stack: dither, value*(int scaler), int scaler + pop edx // clear parameter off stack + mov ecx, sourceEnd // restore ecx + mov eax, sourceByteStride // restore eax + */ + + // generate dither + mov sourceByteStride, eax // save eax + mov edx, 196314165 + mov eax, ditherRandSeed1 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov ditherRandSeed1, eax + mov edx, 196314165 + mov eax, ditherRandSeed2 + mul edx // eax:edx = eax * 196314165 + //add eax, 907633515 + lea eax, [eax+907633515] + mov edx, ditherRandSeed1 + shr edx, PA_DITHER_SHIFT_ + mov ditherRandSeed2, eax + shr eax, PA_DITHER_SHIFT_ + //add eax, edx // eax -> current + lea eax, [eax+edx] // current = randSeed1>>x + randSeed2>>x + mov edx, ditherPrevious + neg edx + lea edx, [eax+edx] // highpass = current - previous + mov highpassedDither, edx + mov ditherPrevious, eax // previous = current + mov eax, sourceByteStride // restore eax + fild highpassedDither + fmul const_float_dither_scale_ + // end generate dither, dither signal in st(0) + + faddp st(1), st(0) // stack: dither * value*(int scaler), int scaler + fistp word ptr [edi] // store scaled int into dest, stack: int scaler + jmp Float32_To_Int16_DitherClip_stored + + Float32_To_Int16_DitherClip_clamp: + mov edx, dword ptr [esi] // load floating point value into integer register + shr edx, 31 // move sign bit into bit 0 + add esi, eax // increment source ptr + //lea esi, [esi+eax] + add dx, 0x7FFF // convert to maximum range integers + mov word ptr [edi], dx // store clamped into into dest + + Float32_To_Int16_DitherClip_stored: + + add edi, ebx // increment destination ptr + //lea edi, [edi+ebx] + + cmp esi, ecx // has src ptr reached end? + jne Float32_To_Int16_DitherClip_loop + + ffree st(0) + fincstp + + fwait + fnclex + fldcw savedFpuControlWord + } + + ditherGenerator->previous = ditherPrevious; + ditherGenerator->randSeed1 = ditherRandSeed1; + ditherGenerator->randSeed2 = ditherRandSeed2; +} + +/* -------------------------------------------------------------------------- */ + +void PaUtil_InitializeX86PlainConverters( void ) +{ + paConverters.Float32_To_Int32 = Float32_To_Int32; + paConverters.Float32_To_Int32_Clip = Float32_To_Int32_Clip; + paConverters.Float32_To_Int32_DitherClip = Float32_To_Int32_DitherClip; + + paConverters.Float32_To_Int24 = Float32_To_Int24; + paConverters.Float32_To_Int24_Clip = Float32_To_Int24_Clip; + paConverters.Float32_To_Int24_DitherClip = Float32_To_Int24_DitherClip; + + paConverters.Float32_To_Int16 = Float32_To_Int16; + paConverters.Float32_To_Int16_Clip = Float32_To_Int16_Clip; + paConverters.Float32_To_Int16_DitherClip = Float32_To_Int16_DitherClip; +} + +/* -------------------------------------------------------------------------- */ diff --git a/pd/portaudio/pa_win/pa_x86_plain_converters.h b/pd/portaudio/pa_win/pa_x86_plain_converters.h new file mode 100644 index 00000000..b93a291c --- /dev/null +++ b/pd/portaudio/pa_win/pa_x86_plain_converters.h @@ -0,0 +1,19 @@ +#ifndef PA_X86_PLAIN_CONVERTERS_H +#define PA_X86_PLAIN_CONVERTERS_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +/** + Install optimised converter functions suitable for all IA32 processors +*/ +void PaUtil_InitializeX86PlainConverters( void ); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PA_X86_PLAIN_CONVERTERS_H */ diff --git a/pd/portaudio/pa_win_ds/dsound_wrapper.c b/pd/portaudio/pa_win_ds/dsound_wrapper.c new file mode 100644 index 00000000..ac8c927d --- /dev/null +++ b/pd/portaudio/pa_win_ds/dsound_wrapper.c @@ -0,0 +1,604 @@ +/* + * $Id: dsound_wrapper.c,v 1.1.1.1.2.5 2002/07/01 00:49:41 philburk Exp $ + * Simplified DirectSound interface. + * + * Author: Phil Burk & Robert Marsanyi + * + * PortAudio Portable Real-Time Audio Library + * For more information see: http://www.softsynth.com/portaudio/ + * DirectSound Implementation + * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include +#include +#include +#define INITGUID // Needed to build IID_IDirectSoundNotify. See objbase.h for info. +#include +#include +#include "dsound_wrapper.h" +#include "pa_trace.h" + +/************************************************************************************/ +DSoundEntryPoints dswDSoundEntryPoints = { 0, 0, 0, 0, 0, 0, 0 }; +/************************************************************************************/ +static HRESULT WINAPI DummyDirectSoundCreate(LPGUID lpcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter) +{ + (void)lpcGuidDevice; /* unused parameter */ + (void)ppDS; /* unused parameter */ + (void)pUnkOuter; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundEnumerateW(LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext) +{ + (void)lpDSEnumCallback; /* unused parameter */ + (void)lpContext; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundEnumerateA(LPDSENUMCALLBACKA lpDSEnumCallback, LPVOID lpContext) +{ + (void)lpDSEnumCallback; /* unused parameter */ + (void)lpContext; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundCaptureCreate(LPGUID lpcGUID, LPDIRECTSOUNDCAPTURE *lplpDSC, LPUNKNOWN pUnkOuter) +{ + (void)lpcGUID; /* unused parameter */ + (void)lplpDSC; /* unused parameter */ + (void)pUnkOuter; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundCaptureEnumerateW(LPDSENUMCALLBACKW lpDSCEnumCallback, LPVOID lpContext) +{ + (void)lpDSCEnumCallback; /* unused parameter */ + (void)lpContext; /* unused parameter */ + return E_NOTIMPL; +} + +static HRESULT WINAPI DummyDirectSoundCaptureEnumerateA(LPDSENUMCALLBACKA lpDSCEnumCallback, LPVOID lpContext) +{ + (void)lpDSCEnumCallback; /* unused parameter */ + (void)lpContext; /* unused parameter */ + return E_NOTIMPL; +} +/************************************************************************************/ +void DSW_InitializeDSoundEntryPoints(void) +{ + dswDSoundEntryPoints.hInstance_ = LoadLibrary("dsound.dll"); + if( dswDSoundEntryPoints.hInstance_ != NULL ) + { + dswDSoundEntryPoints.DirectSoundCreate = + (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCreate" ); + if( dswDSoundEntryPoints.DirectSoundCreate == NULL ) + dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate; + + dswDSoundEntryPoints.DirectSoundEnumerateW = + (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateW" ); + if( dswDSoundEntryPoints.DirectSoundEnumerateW == NULL ) + dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW; + + dswDSoundEntryPoints.DirectSoundEnumerateA = + (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundEnumerateA" ); + if( dswDSoundEntryPoints.DirectSoundEnumerateA == NULL ) + dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA; + + dswDSoundEntryPoints.DirectSoundCaptureCreate = + (HRESULT (WINAPI *)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureCreate" ); + if( dswDSoundEntryPoints.DirectSoundCaptureCreate == NULL ) + dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate; + + dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = + (HRESULT (WINAPI *)(LPDSENUMCALLBACKW, LPVOID)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateW" ); + if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateW == NULL ) + dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW; + + dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = + (HRESULT (WINAPI *)(LPDSENUMCALLBACKA, LPVOID)) + GetProcAddress( dswDSoundEntryPoints.hInstance_, "DirectSoundCaptureEnumerateA" ); + if( dswDSoundEntryPoints.DirectSoundCaptureEnumerateA == NULL ) + dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA; + } + else + { + /* initialize with dummy entry points to make live easy when ds isn't present */ + dswDSoundEntryPoints.DirectSoundCreate = DummyDirectSoundCreate; + dswDSoundEntryPoints.DirectSoundEnumerateW = DummyDirectSoundEnumerateW; + dswDSoundEntryPoints.DirectSoundEnumerateA = DummyDirectSoundEnumerateA; + dswDSoundEntryPoints.DirectSoundCaptureCreate = DummyDirectSoundCaptureCreate; + dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = DummyDirectSoundCaptureEnumerateW; + dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = DummyDirectSoundCaptureEnumerateA; + } +} +/************************************************************************************/ +void DSW_TerminateDSoundEntryPoints(void) +{ + if( dswDSoundEntryPoints.hInstance_ != NULL ) + { + FreeLibrary( dswDSoundEntryPoints.hInstance_ ); + dswDSoundEntryPoints.hInstance_ = NULL; + /* ensure that we crash reliably if the entry points arent initialised */ + dswDSoundEntryPoints.DirectSoundCreate = 0; + dswDSoundEntryPoints.DirectSoundEnumerateW = 0; + dswDSoundEntryPoints.DirectSoundEnumerateA = 0; + dswDSoundEntryPoints.DirectSoundCaptureCreate = 0; + dswDSoundEntryPoints.DirectSoundCaptureEnumerateW = 0; + dswDSoundEntryPoints.DirectSoundCaptureEnumerateA = 0; + } +} +/************************************************************************************/ +void DSW_Term( DSoundWrapper *dsw ) +{ + // Cleanup the sound buffers + if (dsw->dsw_OutputBuffer) + { + IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); + IDirectSoundBuffer_Release( dsw->dsw_OutputBuffer ); + dsw->dsw_OutputBuffer = NULL; + } + + if (dsw->dsw_InputBuffer) + { + IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); + IDirectSoundCaptureBuffer_Release( dsw->dsw_InputBuffer ); + dsw->dsw_InputBuffer = NULL; + } + + if (dsw->dsw_pDirectSoundCapture) + { + IDirectSoundCapture_Release( dsw->dsw_pDirectSoundCapture ); + dsw->dsw_pDirectSoundCapture = NULL; + } + + if (dsw->dsw_pDirectSound) + { + IDirectSound_Release( dsw->dsw_pDirectSound ); + dsw->dsw_pDirectSound = NULL; + } +} +/************************************************************************************/ +HRESULT DSW_Init( DSoundWrapper *dsw ) +{ + memset( dsw, 0, sizeof(DSoundWrapper) ); + return 0; +} +/************************************************************************************/ +HRESULT DSW_InitOutputDevice( DSoundWrapper *dsw, LPGUID lpGUID ) +{ + // Create the DS object + HRESULT hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &dsw->dsw_pDirectSound, NULL ); + if( hr != DS_OK ) return hr; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer ) +{ + DWORD dwDataLen; + DWORD playCursor; + HRESULT result; + LPDIRECTSOUNDBUFFER pPrimaryBuffer; + HWND hWnd; + HRESULT hr; + WAVEFORMATEX wfFormat; + DSBUFFERDESC primaryDesc; + DSBUFFERDESC secondaryDesc; + unsigned char* pDSBuffData; + LARGE_INTEGER counterFrequency; + + dsw->dsw_OutputSize = bytesPerBuffer; + dsw->dsw_OutputRunning = FALSE; + dsw->dsw_OutputUnderflows = 0; + dsw->dsw_FramesWritten = 0; + dsw->dsw_BytesPerOutputFrame = nChannels * sizeof(short); + + // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the + // applications's window. Also if that window is closed before the Buffer is closed + // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.) + // So we will use GetDesktopWindow() which was suggested by Miller Puckette. + // hWnd = GetForegroundWindow(); + hWnd = GetDesktopWindow(); + + // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz. + // Exclusize also prevents unexpected sounds from other apps during a performance. + if ((hr = IDirectSound_SetCooperativeLevel( dsw->dsw_pDirectSound, + hWnd, DSSCL_EXCLUSIVE)) != DS_OK) + { + return hr; + } + + // ----------------------------------------------------------------------- + // Create primary buffer and set format just so we can specify our custom format. + // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz. + // Setup the primary buffer description + ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC)); + primaryDesc.dwSize = sizeof(DSBUFFERDESC); + primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth + primaryDesc.dwBufferBytes = 0; + primaryDesc.lpwfxFormat = NULL; + // Create the buffer + if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, + &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result; + // Define the buffer format + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = nChannels; + wfFormat.nSamplesPerSec = nFrameRate; + wfFormat.wBitsPerSample = 8 * sizeof(short); + wfFormat.nBlockAlign = wfFormat.nChannels * wfFormat.wBitsPerSample / 8; + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.cbSize = 0; /* No extended format info. */ + // Set the primary buffer's format + if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, &wfFormat)) != DS_OK) return result; + + // ---------------------------------------------------------------------- + // Setup the secondary buffer description + ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC)); + secondaryDesc.dwSize = sizeof(DSBUFFERDESC); + secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2; + secondaryDesc.dwBufferBytes = bytesPerBuffer; + secondaryDesc.lpwfxFormat = &wfFormat; + // Create the secondary buffer + if ((result = IDirectSound_CreateSoundBuffer( dsw->dsw_pDirectSound, + &secondaryDesc, &dsw->dsw_OutputBuffer, NULL)) != DS_OK) return result; + // Lock the DS buffer + if ((result = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, 0, dsw->dsw_OutputSize, (LPVOID*)&pDSBuffData, + &dwDataLen, NULL, 0, 0)) != DS_OK) return result; + // Zero the DS buffer + ZeroMemory(pDSBuffData, dwDataLen); + // Unlock the DS buffer + if ((result = IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result; + if( QueryPerformanceFrequency( &counterFrequency ) ) + { + int framesInBuffer = bytesPerBuffer / (nChannels * sizeof(short)); + dsw->dsw_CounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate; + } + else + { + dsw->dsw_CounterTicksPerBuffer.QuadPart = 0; + } + // Let DSound set the starting write position because if we set it to zero, it looks like the + // buffer is full to begin with. This causes a long pause before sound starts when using large buffers. + hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &dsw->dsw_WriteOffset ); + if( hr != DS_OK ) + { + return hr; + } + dsw->dsw_FramesWritten = dsw->dsw_WriteOffset / dsw->dsw_BytesPerOutputFrame; + /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */ + return DS_OK; +} + +/************************************************************************************/ +HRESULT DSW_StartOutput( DSoundWrapper *dsw ) +{ + HRESULT hr; + QueryPerformanceCounter( &dsw->dsw_LastPlayTime ); + dsw->dsw_LastPlayCursor = 0; + dsw->dsw_FramesPlayed = 0; + hr = IDirectSoundBuffer_SetCurrentPosition( dsw->dsw_OutputBuffer, 0 ); + if( hr != DS_OK ) + { + return hr; + } + // Start the buffer playback in a loop. + if( dsw->dsw_OutputBuffer != NULL ) + { + hr = IDirectSoundBuffer_Play( dsw->dsw_OutputBuffer, 0, 0, DSBPLAY_LOOPING ); + if( hr != DS_OK ) + { + return hr; + } + dsw->dsw_OutputRunning = TRUE; + } + + return 0; +} +/************************************************************************************/ +HRESULT DSW_StopOutput( DSoundWrapper *dsw ) +{ + // Stop the buffer playback + if( dsw->dsw_OutputBuffer != NULL ) + { + dsw->dsw_OutputRunning = FALSE; + return IDirectSoundBuffer_Stop( dsw->dsw_OutputBuffer ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilledPtr ) +{ + HRESULT hr; + DWORD playCursor; + DWORD writeCursor; + long bytesFilled; + // Query to see where play position is. + // We don't need the writeCursor but sometimes DirectSound doesn't handle NULLS correctly + // so let's pass a pointer just to be safe. + hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor ); + if( hr != DS_OK ) + { + return hr; + } + bytesFilled = dsw->dsw_WriteOffset - playCursor; + if( bytesFilled < 0 ) bytesFilled += dsw->dsw_OutputSize; // unwrap offset + *bytesFilledPtr = bytesFilled; + return hr; +} + +/************************************************************************************ + * Determine how much space can be safely written to in DS buffer. + * Detect underflows and overflows. + * Does not allow writing into safety gap maintained by DirectSound. + */ +HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ) +{ + HRESULT hr; + DWORD playCursor; + DWORD writeCursor; + long numBytesEmpty; + long playWriteGap; + // Query to see how much room is in buffer. + hr = IDirectSoundBuffer_GetCurrentPosition( dsw->dsw_OutputBuffer, &playCursor, &writeCursor ); + if( hr != DS_OK ) + { + return hr; + } + // Determine size of gap between playIndex and WriteIndex that we cannot write into. + playWriteGap = writeCursor - playCursor; + if( playWriteGap < 0 ) playWriteGap += dsw->dsw_OutputSize; // unwrap + /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */ + /* Attempt to detect playCursor wrap-around and correct it. */ + if( dsw->dsw_OutputRunning && (dsw->dsw_CounterTicksPerBuffer.QuadPart != 0) ) + { + /* How much time has elapsed since last check. */ + LARGE_INTEGER currentTime; + LARGE_INTEGER elapsedTime; + long bytesPlayed; + long bytesExpected; + long buffersWrapped; + QueryPerformanceCounter( ¤tTime ); + elapsedTime.QuadPart = currentTime.QuadPart - dsw->dsw_LastPlayTime.QuadPart; + dsw->dsw_LastPlayTime = currentTime; + /* How many bytes does DirectSound say have been played. */ + bytesPlayed = playCursor - dsw->dsw_LastPlayCursor; + if( bytesPlayed < 0 ) bytesPlayed += dsw->dsw_OutputSize; // unwrap + dsw->dsw_LastPlayCursor = playCursor; + /* Calculate how many bytes we would have expected to been played by now. */ + bytesExpected = (long) ((elapsedTime.QuadPart * dsw->dsw_OutputSize) / dsw->dsw_CounterTicksPerBuffer.QuadPart); + buffersWrapped = (bytesExpected - bytesPlayed) / dsw->dsw_OutputSize; + if( buffersWrapped > 0 ) + { + playCursor += (buffersWrapped * dsw->dsw_OutputSize); + bytesPlayed += (buffersWrapped * dsw->dsw_OutputSize); + } + /* Maintain frame output cursor. */ + dsw->dsw_FramesPlayed += (bytesPlayed / dsw->dsw_BytesPerOutputFrame); + } + numBytesEmpty = playCursor - dsw->dsw_WriteOffset; + if( numBytesEmpty < 0 ) numBytesEmpty += dsw->dsw_OutputSize; // unwrap offset + /* Have we underflowed? */ + if( numBytesEmpty > (dsw->dsw_OutputSize - playWriteGap) ) + { + if( dsw->dsw_OutputRunning ) + { + dsw->dsw_OutputUnderflows += 1; + } + dsw->dsw_WriteOffset = writeCursor; + numBytesEmpty = dsw->dsw_OutputSize - playWriteGap; + } + *bytesEmpty = numBytesEmpty; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + long bytesEmpty; + hr = DSW_QueryOutputSpace( dsw, &bytesEmpty ); // updates dsw_FramesPlayed + if (hr != DS_OK) return hr; + if( bytesEmpty == 0 ) return DS_OK; + // Lock free space in the DS + hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, bytesEmpty, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy the buffer into the DS + ZeroMemory(lpbuf1, dwsize1); + if(lpbuf2 != NULL) + { + ZeroMemory(lpbuf2, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize; + IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + dsw->dsw_FramesWritten += bytesEmpty / dsw->dsw_BytesPerOutputFrame; + } + return hr; +} + +/************************************************************************************/ +HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + // Lock free space in the DS + hr = IDirectSoundBuffer_Lock( dsw->dsw_OutputBuffer, dsw->dsw_WriteOffset, numBytes, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy the buffer into the DS + CopyMemory(lpbuf1, buf, dwsize1); + if(lpbuf2 != NULL) + { + CopyMemory(lpbuf2, buf+dwsize1, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + dwsize1 + dwsize2) % dsw->dsw_OutputSize; + IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + dsw->dsw_FramesWritten += numBytes / dsw->dsw_BytesPerOutputFrame; + } + return hr; +} + +/************************************************************************************/ +DWORD DSW_GetOutputStatus( DSoundWrapper *dsw ) +{ + DWORD status; + if (IDirectSoundBuffer_GetStatus( dsw->dsw_OutputBuffer, &status ) != DS_OK) + return( DSERR_INVALIDPARAM ); + else + return( status ); +} + +/* These routines are used to support audio input. + * Do NOT compile these calls when using NT4 because it does + * not support the entry points. + */ +/************************************************************************************/ +HRESULT DSW_InitInputDevice( DSoundWrapper *dsw, LPGUID lpGUID ) +{ + HRESULT hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &dsw->dsw_pDirectSoundCapture, NULL ); + if( hr != DS_OK ) return hr; + return hr; +} +/************************************************************************************/ +HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, int nChannels, int bytesPerBuffer ) +{ + DSCBUFFERDESC captureDesc; + WAVEFORMATEX wfFormat; + HRESULT result; + + dsw->dsw_BytesPerInputFrame = nChannels * sizeof(short); + + // Define the buffer format + wfFormat.wFormatTag = WAVE_FORMAT_PCM; + wfFormat.nChannels = nChannels; + wfFormat.nSamplesPerSec = nFrameRate; + wfFormat.wBitsPerSample = 8 * sizeof(short); + wfFormat.nBlockAlign = wfFormat.nChannels * (wfFormat.wBitsPerSample / 8); + wfFormat.nAvgBytesPerSec = wfFormat.nSamplesPerSec * wfFormat.nBlockAlign; + wfFormat.cbSize = 0; /* No extended format info. */ + dsw->dsw_InputSize = bytesPerBuffer; + // ---------------------------------------------------------------------- + // Setup the secondary buffer description + ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC)); + captureDesc.dwSize = sizeof(DSCBUFFERDESC); + captureDesc.dwFlags = 0; + captureDesc.dwBufferBytes = bytesPerBuffer; + captureDesc.lpwfxFormat = &wfFormat; + // Create the capture buffer + if ((result = IDirectSoundCapture_CreateCaptureBuffer( dsw->dsw_pDirectSoundCapture, + &captureDesc, &dsw->dsw_InputBuffer, NULL)) != DS_OK) return result; + dsw->dsw_ReadOffset = 0; // reset last read position to start of buffer + return DS_OK; +} + +/************************************************************************************/ +HRESULT DSW_StartInput( DSoundWrapper *dsw ) +{ + // Start the buffer playback + if( dsw->dsw_InputBuffer != NULL ) + { + return IDirectSoundCaptureBuffer_Start( dsw->dsw_InputBuffer, DSCBSTART_LOOPING ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_StopInput( DSoundWrapper *dsw ) +{ + // Stop the buffer playback + if( dsw->dsw_InputBuffer != NULL ) + { + return IDirectSoundCaptureBuffer_Stop( dsw->dsw_InputBuffer ); + } + else return 0; +} + +/************************************************************************************/ +HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled ) +{ + HRESULT hr; + DWORD capturePos; + DWORD readPos; + long filled; + // Query to see how much data is in buffer. + // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly + // so let's pass a pointer just to be safe. + hr = IDirectSoundCaptureBuffer_GetCurrentPosition( dsw->dsw_InputBuffer, &capturePos, &readPos ); + if( hr != DS_OK ) + { + return hr; + } + filled = readPos - dsw->dsw_ReadOffset; + if( filled < 0 ) filled += dsw->dsw_InputSize; // unwrap offset + *bytesFilled = filled; + return hr; +} + +/************************************************************************************/ +HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes ) +{ + HRESULT hr; + LPBYTE lpbuf1 = NULL; + LPBYTE lpbuf2 = NULL; + DWORD dwsize1 = 0; + DWORD dwsize2 = 0; + // Lock free space in the DS + hr = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, dsw->dsw_ReadOffset, numBytes, (void **) &lpbuf1, &dwsize1, + (void **) &lpbuf2, &dwsize2, 0); + if (hr == DS_OK) + { + // Copy from DS to the buffer + CopyMemory( buf, lpbuf1, dwsize1); + if(lpbuf2 != NULL) + { + CopyMemory( buf+dwsize1, lpbuf2, dwsize2); + } + // Update our buffer offset and unlock sound buffer + dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + dwsize1 + dwsize2) % dsw->dsw_InputSize; + IDirectSoundCaptureBuffer_Unlock ( dsw->dsw_InputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2); + } + return hr; +} + diff --git a/pd/portaudio/pa_win_ds/dsound_wrapper.h b/pd/portaudio/pa_win_ds/dsound_wrapper.h new file mode 100644 index 00000000..e9ce4c6b --- /dev/null +++ b/pd/portaudio/pa_win_ds/dsound_wrapper.h @@ -0,0 +1,129 @@ +#ifndef __DSOUND_WRAPPER_H +#define __DSOUND_WRAPPER_H +/* + * $Id: dsound_wrapper.h,v 1.1.1.1.2.5 2002/07/03 01:43:56 rossbencina Exp $ + * Simplified DirectSound interface. + * + * Author: Phil Burk & Robert Marsanyi + * + * For PortAudio Portable Real-Time Audio Library + * For more information see: http://www.softsynth.com/portaudio/ + * DirectSound Implementation + * Copyright (c) 1999-2000 Phil Burk & Robert Marsanyi + * + * 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. + * + */ + +/* on Borland compilers, WIN32 doesn't seem to be defined by default, which + breaks DSound.h. Adding the define here fixes the problem. - rossb. */ +#ifdef __BORLANDC__ +#if !defined(WIN32) +#define WIN32 +#endif +#endif + +#include +#if !defined(BOOL) +#define BOOL short +#endif + + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + + +typedef struct +{ + HINSTANCE hInstance_; + + HRESULT (WINAPI *DirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); + HRESULT (WINAPI *DirectSoundEnumerateW)(LPDSENUMCALLBACKW, LPVOID); + HRESULT (WINAPI *DirectSoundEnumerateA)(LPDSENUMCALLBACKA, LPVOID); + + HRESULT (WINAPI *DirectSoundCaptureCreate)(LPGUID, LPDIRECTSOUNDCAPTURE *, LPUNKNOWN); + HRESULT (WINAPI *DirectSoundCaptureEnumerateW)(LPDSENUMCALLBACKW, LPVOID); + HRESULT (WINAPI *DirectSoundCaptureEnumerateA)(LPDSENUMCALLBACKA, LPVOID); +}DSoundEntryPoints; + +extern DSoundEntryPoints dswDSoundEntryPoints; + +void DSW_InitializeDSoundEntryPoints(void); +void DSW_TerminateDSoundEntryPoints(void); + +#define DSW_NUM_POSITIONS (4) +#define DSW_NUM_EVENTS (5) +#define DSW_TERMINATION_EVENT (DSW_NUM_POSITIONS) + +typedef struct +{ +/* Output */ + LPDIRECTSOUND dsw_pDirectSound; + LPDIRECTSOUNDBUFFER dsw_OutputBuffer; + DWORD dsw_WriteOffset; /* last write position */ + INT dsw_OutputSize; + INT dsw_BytesPerOutputFrame; + /* Try to detect play buffer underflows. */ + LARGE_INTEGER dsw_CounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */ + LARGE_INTEGER dsw_LastPlayTime; + UINT dsw_LastPlayCursor; + UINT dsw_OutputUnderflows; + BOOL dsw_OutputRunning; + /* use double which lets us can play for several thousand years with enough precision */ + double dsw_FramesWritten; + double dsw_FramesPlayed; +/* Input */ + INT dsw_BytesPerInputFrame; + LPDIRECTSOUNDCAPTURE dsw_pDirectSoundCapture; + LPDIRECTSOUNDCAPTUREBUFFER dsw_InputBuffer; + UINT dsw_ReadOffset; /* last read position */ + UINT dsw_InputSize; +} DSoundWrapper; + +HRESULT DSW_Init( DSoundWrapper *dsw ); +void DSW_Term( DSoundWrapper *dsw ); +HRESULT DSW_InitOutputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, + int nChannels, int bufSize ); +HRESULT DSW_StartOutput( DSoundWrapper *dsw ); +HRESULT DSW_StopOutput( DSoundWrapper *dsw ); +DWORD DSW_GetOutputStatus( DSoundWrapper *dsw ); +HRESULT DSW_WriteBlock( DSoundWrapper *dsw, char *buf, long numBytes ); +HRESULT DSW_ZeroEmptySpace( DSoundWrapper *dsw ); +HRESULT DSW_QueryOutputSpace( DSoundWrapper *dsw, long *bytesEmpty ); +HRESULT DSW_Enumerate( DSoundWrapper *dsw ); + +HRESULT DSW_InitInputBuffer( DSoundWrapper *dsw, unsigned long nFrameRate, + int nChannels, int bufSize ); +HRESULT DSW_StartInput( DSoundWrapper *dsw ); +HRESULT DSW_StopInput( DSoundWrapper *dsw ); +HRESULT DSW_ReadBlock( DSoundWrapper *dsw, char *buf, long numBytes ); +HRESULT DSW_QueryInputFilled( DSoundWrapper *dsw, long *bytesFilled ); +HRESULT DSW_QueryOutputFilled( DSoundWrapper *dsw, long *bytesFilled ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* __DSOUND_WRAPPER_H */ diff --git a/pd/portaudio/pa_win_ds/pa_dsound.c b/pd/portaudio/pa_win_ds/pa_dsound.c new file mode 100644 index 00000000..8fa343cf --- /dev/null +++ b/pd/portaudio/pa_win_ds/pa_dsound.c @@ -0,0 +1,1021 @@ +/* + * $Id: pa_dsound.c,v 1.1.1.1 2003-05-09 16:03:58 ggeiger Exp $ + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.softsynth.com/portaudio/ + * DirectSound Implementation + * + * Copyright (c) 1999-2000 Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +/* Modifications + * 7/19/01 Mike Berry - casts for compiling with __MWERKS__ CodeWarrior + * 9/27/01 Phil Burk - use number of frames instead of real-time for CPULoad calculation. + * 4/19/02 Phil Burk - Check for Win XP for system latency calculation. + */ +/* Compiler flags: + SUPPORT_AUDIO_CAPTURE - define this flag if you want to SUPPORT_AUDIO_CAPTURE + */ + +#include +#include +#ifndef __MWERKS__ +#include +#include +#endif //__MWERKS__ +#include +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" +#include "dsound_wrapper.h" + +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +#define PA_USE_HIGH_LATENCY (0) +#if PA_USE_HIGH_LATENCY +#define PA_WIN_9X_LATENCY (500) +#define PA_WIN_NT_LATENCY (600) +#else +#define PA_WIN_9X_LATENCY (140) +#define PA_WIN_NT_LATENCY (280) +#endif + +#define PA_WIN_WDM_LATENCY (120) + +/* Trigger an underflow for testing purposes. Should normally be (0). */ +#define PA_SIMULATE_UNDERFLOW (0) +#if PA_SIMULATE_UNDERFLOW +static gUnderCallbackCounter = 0; +#define UNDER_START_GAP (10) +#define UNDER_STOP_GAP (UNDER_START_GAP + 4) +#endif + +/************************************************* Definitions ********/ +typedef struct internalPortAudioStream internalPortAudioStream; +typedef struct internalPortAudioDevice +{ + GUID pad_GUID; + GUID *pad_lpGUID; + double pad_SampleRates[10]; /* for pointing to from pad_Info FIXME?!*/ + PaDeviceInfo pad_Info; +} +internalPortAudioDevice; + +/* Define structure to contain all DirectSound and Windows specific data. */ +typedef struct PaHostSoundControl +{ + DSoundWrapper pahsc_DSoundWrapper; + MMRESULT pahsc_TimerID; + BOOL pahsc_IfInsideCallback; /* Test for reentrancy. */ + short *pahsc_NativeBuffer; + unsigned int pahsc_BytesPerBuffer; /* native buffer size in bytes */ + double pahsc_ValidFramesWritten; + int pahsc_FramesPerDSBuffer; + /* For measuring CPU utilization. */ + LARGE_INTEGER pahsc_EntryCount; + double pahsc_InverseTicksPerUserBuffer; +} +PaHostSoundControl; + +/************************************************* Shared Data ********/ +/* FIXME - put Mutex around this shared data. */ +static int sNumDevices = 0; +static int sDeviceIndex = 0; +static internalPortAudioDevice *sDevices = NULL; +static int sDefaultInputDeviceID = paNoDevice; +static int sDefaultOutputDeviceID = paNoDevice; +static int sEnumerationError; +static int sPaHostError = 0; +/************************************************* Prototypes **********/ +static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id ); +static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ); +static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ); +static Pa_QueryDevices( void ); +static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, + DWORD dwUser, DWORD dw1, DWORD dw2); + +/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ +static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + /* Query system timer for usage analysis and to prevent overuse of CPU. */ + QueryPerformanceCounter( &pahsc->pahsc_EntryCount ); +} + +static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +{ + LARGE_INTEGER CurrentCount = { 0, 0 }; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + /* + ** Measure CPU utilization during this callback. Note that this calculation + ** assumes that we had the processor the whole time. + */ +#define LOWPASS_COEFFICIENT_0 (0.9) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + if( QueryPerformanceCounter( &CurrentCount ) ) + { + LONGLONG InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart; + double newUsage = InsideCount * pahsc->pahsc_InverseTicksPerUserBuffer; + past->past_Usage = (LOWPASS_COEFFICIENT_0 * past->past_Usage) + + (LOWPASS_COEFFICIENT_1 * newUsage); + } +} + +/****************************************** END CPU UTILIZATION *******/ +static PaError Pa_QueryDevices( void ) +{ + int numBytes; + sDefaultInputDeviceID = paNoDevice; + sDefaultOutputDeviceID = paNoDevice; + /* Enumerate once just to count devices. */ + sNumDevices = 0; // for default device + DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL ); +#if SUPPORT_AUDIO_CAPTURE + DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, NULL ); +#endif /* SUPPORT_AUDIO_CAPTURE */ + /* Allocate structures to hold device info. */ + numBytes = sNumDevices * sizeof(internalPortAudioDevice); + sDevices = (internalPortAudioDevice *)PaHost_AllocateFastMemory( numBytes ); /* MEM */ + if( sDevices == NULL ) return paInsufficientMemory; + /* Enumerate again to fill in structures. */ + sDeviceIndex = 0; + sEnumerationError = 0; + DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)0 ); +#if SUPPORT_AUDIO_CAPTURE + if( sEnumerationError != paNoError ) return sEnumerationError; + sEnumerationError = 0; + DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_EnumProc, (void *)1 ); +#endif /* SUPPORT_AUDIO_CAPTURE */ + return sEnumerationError; +} +/************************************************************************************/ +long Pa_GetHostError() +{ + return sPaHostError; +} +/************************************************************************************ +** Just count devices so we know how much memory to allocate. +*/ +static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + sNumDevices++; + return TRUE; +} +/************************************************************************************ +** Extract capabilities info from each device. +*/ +static BOOL CALLBACK Pa_EnumProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + HRESULT hr; + LPDIRECTSOUND lpDirectSound; +#if SUPPORT_AUDIO_CAPTURE + LPDIRECTSOUNDCAPTURE lpDirectSoundCapture; +#endif /* SUPPORT_AUDIO_CAPTURE */ + int isInput = (int) lpContext; /* Passed from Pa_CountDevices() */ + internalPortAudioDevice *pad; + + if( sDeviceIndex >= sNumDevices ) + { + sEnumerationError = paInternalError; + return FALSE; + } + pad = &sDevices[sDeviceIndex]; + /* Copy GUID to static array. Set pointer. */ + if( lpGUID == NULL ) + { + pad->pad_lpGUID = NULL; + } + else + { + memcpy( &pad->pad_GUID, lpGUID, sizeof(GUID) ); + pad->pad_lpGUID = &pad->pad_GUID; + } + pad->pad_Info.sampleRates = pad->pad_SampleRates; /* Point to array. */ + /* Allocate room for descriptive name. */ + if( lpszDesc != NULL ) + { + int len = strlen(lpszDesc); + pad->pad_Info.name = (char *)malloc( len+1 ); + if( pad->pad_Info.name == NULL ) + { + sEnumerationError = paInsufficientMemory; + return FALSE; + } + memcpy( (void *) pad->pad_Info.name, lpszDesc, len+1 ); + } +#if SUPPORT_AUDIO_CAPTURE + if( isInput ) + { + /********** Input ******************************/ + DSCCAPS caps; + if( lpGUID == NULL ) sDefaultInputDeviceID = sDeviceIndex; + hr = DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL ); + if( hr != DS_OK ) + { + pad->pad_Info.maxInputChannels = 0; + DBUG(("Cannot create Capture for %s. Result = 0x%x\n", lpszDesc, hr )); + } + else + { + /* Query device characteristics. */ + caps.dwSize = sizeof(caps); + IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps ); + /* printf("caps.dwFormats = 0x%x\n", caps.dwFormats ); */ + pad->pad_Info.maxInputChannels = caps.dwChannels; + /* Determine sample rates from flags. */ + if( caps.dwChannels == 2 ) + { + int index = 0; + if( caps.dwFormats & WAVE_FORMAT_1S16) pad->pad_SampleRates[index++] = 11025.0; + if( caps.dwFormats & WAVE_FORMAT_2S16) pad->pad_SampleRates[index++] = 22050.0; + if( caps.dwFormats & WAVE_FORMAT_4S16) pad->pad_SampleRates[index++] = 44100.0; + pad->pad_Info.numSampleRates = index; + } + else if( caps.dwChannels == 1 ) + { + int index = 0; + if( caps.dwFormats & WAVE_FORMAT_1M16) pad->pad_SampleRates[index++] = 11025.0; + if( caps.dwFormats & WAVE_FORMAT_2M16) pad->pad_SampleRates[index++] = 22050.0; + if( caps.dwFormats & WAVE_FORMAT_4M16) pad->pad_SampleRates[index++] = 44100.0; + pad->pad_Info.numSampleRates = index; + } + else pad->pad_Info.numSampleRates = 0; + IDirectSoundCapture_Release( lpDirectSoundCapture ); + } + } + else +#endif /* SUPPORT_AUDIO_CAPTURE */ + + { + /********** Output ******************************/ + DSCAPS caps; + if( lpGUID == NULL ) sDefaultOutputDeviceID = sDeviceIndex; + /* Create interfaces for each object. */ + hr = DirectSoundCreate( lpGUID, &lpDirectSound, NULL ); + if( hr != DS_OK ) + { + pad->pad_Info.maxOutputChannels = 0; + DBUG(("Cannot create dsound for %s. Result = 0x%x\n", lpszDesc, hr )); + } + else + { + /* Query device characteristics. */ + caps.dwSize = sizeof(caps); + IDirectSound_GetCaps( lpDirectSound, &caps ); + pad->pad_Info.maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; + /* Get sample rates. */ + pad->pad_SampleRates[0] = (double) caps.dwMinSecondarySampleRate; + pad->pad_SampleRates[1] = (double) caps.dwMaxSecondarySampleRate; + if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) pad->pad_Info.numSampleRates = -1; + else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate ) + { + if( caps.dwMinSecondarySampleRate == 0 ) + { + /* + ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !! + ** But it supports continuous sampling. + ** So fake range of rates, and hope it really supports it. + */ + pad->pad_SampleRates[0] = 11025.0f; + pad->pad_SampleRates[1] = 48000.0f; + pad->pad_Info.numSampleRates = -1; /* continuous range */ + + DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex )); + } + else + { + pad->pad_Info.numSampleRates = 1; + } + } + else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) ) + { + /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000. + ** But we know that they really support a range of rates! + ** So when we see a ridiculous set of rates, assume it is a range. + */ + pad->pad_Info.numSampleRates = -1; + DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex )); + } + else pad->pad_Info.numSampleRates = 2; + IDirectSound_Release( lpDirectSound ); + } + } + pad->pad_Info.nativeSampleFormats = paInt16; + sDeviceIndex++; + return( TRUE ); +} +/*************************************************************************/ +int Pa_CountDevices() +{ + if( sNumDevices <= 0 ) Pa_Initialize(); + return sNumDevices; +} +static internalPortAudioDevice *Pa_GetInternalDevice( PaDeviceID id ) +{ + if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL; + return &sDevices[id]; +} +/*************************************************************************/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + internalPortAudioDevice *pad; + if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL; + pad = Pa_GetInternalDevice( id ); + return &pad->pad_Info ; +} +static PaError Pa_MaybeQueryDevices( void ) +{ + if( sNumDevices == 0 ) + { + return Pa_QueryDevices(); + } + return 0; +} +/************************************************************************* +** Returns recommended device ID. +** On the PC, the recommended device can be specified by the user by +** setting an environment variable. For example, to use device #1. +** +** set PA_RECOMMENDED_OUTPUT_DEVICE=1 +** +** The user should first determine the available device ID by using +** the supplied application "pa_devs". +*/ +#define PA_ENV_BUF_SIZE (32) +#define PA_REC_IN_DEV_ENV_NAME ("PA_RECOMMENDED_INPUT_DEVICE") +#define PA_REC_OUT_DEV_ENV_NAME ("PA_RECOMMENDED_OUTPUT_DEVICE") +static PaDeviceID PaHost_GetEnvDefaultDeviceID( char *envName ) +{ + DWORD hresult; + char envbuf[PA_ENV_BUF_SIZE]; + PaDeviceID recommendedID = paNoDevice; + /* Let user determine default device by setting environment variable. */ + hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + recommendedID = atoi( envbuf ); + } + return recommendedID; +} +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + PaError result; + result = PaHost_GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME ); + if( result < 0 ) + { + result = Pa_MaybeQueryDevices(); + if( result < 0 ) return result; + result = sDefaultInputDeviceID; + } + return result; +} +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + PaError result; + result = PaHost_GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME ); + if( result < 0 ) + { + result = Pa_MaybeQueryDevices(); + if( result < 0 ) return result; + result = sDefaultOutputDeviceID; + } + return result; +} +/********************************************************************** +** Make sure that we have queried the device capabilities. +*/ +PaError PaHost_Init( void ) +{ +#if PA_SIMULATE_UNDERFLOW + PRINT(("WARNING - Underflow Simulation Enabled - Expect a Big Glitch!!!\n")); +#endif + return Pa_MaybeQueryDevices(); +} +static PaError Pa_TimeSlice( internalPortAudioStream *past ) +{ + PaError result = 0; + long bytesEmpty = 0; + long bytesFilled = 0; + long bytesToXfer = 0; + long numChunks; + HRESULT hresult; + PaHostSoundControl *pahsc; + short *nativeBufPtr; + past->past_NumCallbacks += 1; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + /* How much input data is available? */ +#if SUPPORT_AUDIO_CAPTURE + if( past->past_NumInputChannels > 0 ) + { + DSW_QueryInputFilled( &pahsc->pahsc_DSoundWrapper, &bytesFilled ); + bytesToXfer = bytesFilled; + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + /* How much output room is available? */ + if( past->past_NumOutputChannels > 0 ) + { + DSW_QueryOutputSpace( &pahsc->pahsc_DSoundWrapper, &bytesEmpty ); + bytesToXfer = bytesEmpty; + } + AddTraceMessage( "bytesEmpty ", bytesEmpty ); + /* Choose smallest value if both are active. */ + if( (past->past_NumInputChannels > 0) && (past->past_NumOutputChannels > 0) ) + { + bytesToXfer = ( bytesFilled < bytesEmpty ) ? bytesFilled : bytesEmpty; + } + /* printf("bytesFilled = %d, bytesEmpty = %d, bytesToXfer = %d\n", + bytesFilled, bytesEmpty, bytesToXfer); + */ + /* Quantize to multiples of a buffer. */ + numChunks = bytesToXfer / pahsc->pahsc_BytesPerBuffer; + if( numChunks > (long)(past->past_NumUserBuffers/2) ) + { + numChunks = (long)past->past_NumUserBuffers/2; + } + else if( numChunks < 0 ) + { + numChunks = 0; + } + AddTraceMessage( "numChunks ", numChunks ); + nativeBufPtr = pahsc->pahsc_NativeBuffer; + if( numChunks > 0 ) + { + while( numChunks-- > 0 ) + { + /* Measure usage based on time to process one user buffer. */ + Pa_StartUsageCalculation( past ); +#if SUPPORT_AUDIO_CAPTURE + /* Get native data from DirectSound. */ + if( past->past_NumInputChannels > 0 ) + { + hresult = DSW_ReadBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer ); + if( hresult < 0 ) + { + ERR_RPT(("DirectSound ReadBlock failed, hresult = 0x%x\n",hresult)); + sPaHostError = hresult; + break; + } + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + /* Convert 16 bit native data to user data and call user routine. */ + result = Pa_CallConvertInt16( past, nativeBufPtr, nativeBufPtr ); + if( result != 0) break; + /* Pass native data to DirectSound. */ + if( past->past_NumOutputChannels > 0 ) + { + /* static short DEBUGHACK = 0; + DEBUGHACK += 0x0049; + nativeBufPtr[0] = DEBUGHACK; /* Make buzz to see if DirectSound still running. */ + hresult = DSW_WriteBlock( &pahsc->pahsc_DSoundWrapper, (char *) nativeBufPtr, pahsc->pahsc_BytesPerBuffer ); + if( hresult < 0 ) + { + ERR_RPT(("DirectSound WriteBlock failed, result = 0x%x\n",hresult)); + sPaHostError = hresult; + break; + } + } + Pa_EndUsageCalculation( past ); + } + } + return result; +} +/*******************************************************************/ +static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + internalPortAudioStream *past; + PaHostSoundControl *pahsc; +#if PA_SIMULATE_UNDERFLOW + gUnderCallbackCounter++; + if( (gUnderCallbackCounter >= UNDER_START_GAP) && + (gUnderCallbackCounter <= UNDER_STOP_GAP) ) + { + if( gUnderCallbackCounter == UNDER_START_GAP) + { + AddTraceMessage("Begin stall: gUnderCallbackCounter =======", gUnderCallbackCounter ); + } + if( gUnderCallbackCounter == UNDER_STOP_GAP) + { + AddTraceMessage("End stall: gUnderCallbackCounter =======", gUnderCallbackCounter ); + } + return; + } +#endif + past = (internalPortAudioStream *) dwUser; + if( past == NULL ) return; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + if( !pahsc->pahsc_IfInsideCallback && past->past_IsActive ) + { + if( past->past_StopNow ) + { + past->past_IsActive = 0; + } + else if( past->past_StopSoon ) + { + DSoundWrapper *dsw = &pahsc->pahsc_DSoundWrapper; + if( past->past_NumOutputChannels > 0 ) + { + DSW_ZeroEmptySpace( dsw ); + AddTraceMessage("Pa_TimerCallback: waiting - written ", (int) dsw->dsw_FramesWritten ); + AddTraceMessage("Pa_TimerCallback: waiting - played ", (int) dsw->dsw_FramesPlayed ); + /* clear past_IsActive when all sound played */ + if( dsw->dsw_FramesPlayed >= past->past_FrameCount ) + { + past->past_IsActive = 0; + } + } + else + { + past->past_IsActive = 0; + } + } + else + { + pahsc->pahsc_IfInsideCallback = 1; + if( Pa_TimeSlice( past ) != 0) /* Call time slice independant of timing method. */ + { + past->past_StopSoon = 1; + } + pahsc->pahsc_IfInsideCallback = 0; + } + } +} +/*******************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *past ) +{ + HRESULT hr; + PaError result = paNoError; + PaHostSoundControl *pahsc; + int numBytes, maxChannels; + unsigned int minNumBuffers; + internalPortAudioDevice *pad; + DSoundWrapper *dsw; + /* Allocate and initialize host data. */ + pahsc = (PaHostSoundControl *) PaHost_AllocateFastMemory(sizeof(PaHostSoundControl)); /* MEM */ + if( pahsc == NULL ) + { + result = paInsufficientMemory; + goto error; + } + memset( pahsc, 0, sizeof(PaHostSoundControl) ); + past->past_DeviceData = (void *) pahsc; + pahsc->pahsc_TimerID = 0; + dsw = &pahsc->pahsc_DSoundWrapper; + DSW_Init( dsw ); + /* Allocate native buffer. */ + maxChannels = ( past->past_NumOutputChannels > past->past_NumInputChannels ) ? + past->past_NumOutputChannels : past->past_NumInputChannels; + pahsc->pahsc_BytesPerBuffer = past->past_FramesPerUserBuffer * maxChannels * sizeof(short); + if( maxChannels > 0 ) + { + pahsc->pahsc_NativeBuffer = (short *) PaHost_AllocateFastMemory(pahsc->pahsc_BytesPerBuffer); /* MEM */ + if( pahsc->pahsc_NativeBuffer == NULL ) + { + result = paInsufficientMemory; + goto error; + } + } + else + { + result = paInvalidChannelCount; + goto error; + } + + DBUG(("PaHost_OpenStream: pahsc_MinFramesPerHostBuffer = %d\n", pahsc->pahsc_MinFramesPerHostBuffer )); + minNumBuffers = Pa_GetMinNumBuffers( past->past_FramesPerUserBuffer, past->past_SampleRate ); + past->past_NumUserBuffers = ( minNumBuffers > past->past_NumUserBuffers ) ? minNumBuffers : past->past_NumUserBuffers; + numBytes = pahsc->pahsc_BytesPerBuffer * past->past_NumUserBuffers; + if( numBytes < DSBSIZE_MIN ) + { + result = paBufferTooSmall; + goto error; + } + if( numBytes > DSBSIZE_MAX ) + { + result = paBufferTooBig; + goto error; + } + pahsc->pahsc_FramesPerDSBuffer = past->past_FramesPerUserBuffer * past->past_NumUserBuffers; + { + int msecLatency = (int) ((pahsc->pahsc_FramesPerDSBuffer * 1000) / past->past_SampleRate); + PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", pahsc->pahsc_FramesPerDSBuffer, msecLatency )); + } + /* ------------------ OUTPUT */ + if( (past->past_OutputDeviceID >= 0) && (past->past_NumOutputChannels > 0) ) + { + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", past->past_OutputDeviceID)); + pad = Pa_GetInternalDevice( past->past_OutputDeviceID ); + hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL ); + /* If this fails, then try each output device until we find one that works. */ + if( hr != DS_OK ) + { + int i; + ERR_RPT(("Creation of requested Audio Output device '%s' failed.\n", + ((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) )); + for( i=0; ipad_Info.maxOutputChannels >= past->past_NumOutputChannels ) + { + DBUG(("Try device '%s' instead.\n", pad->pad_Info.name )); + hr = DirectSoundCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSound, NULL ); + if( hr == DS_OK ) + { + ERR_RPT(("Using device '%s' instead.\n", pad->pad_Info.name )); + break; + } + } + } + } + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n")); + result = paHostError; + sPaHostError = hr; + goto error; + } + hr = DSW_InitOutputBuffer( dsw, + (unsigned long) (past->past_SampleRate + 0.5), + past->past_NumOutputChannels, numBytes ); + DBUG(("DSW_InitOutputBuffer() returns %x\n", hr)); + if( hr != DS_OK ) + { + result = paHostError; + sPaHostError = hr; + goto error; + } + past->past_FrameCount = pahsc->pahsc_DSoundWrapper.dsw_FramesWritten; + } +#if SUPPORT_AUDIO_CAPTURE + /* ------------------ INPUT */ + if( (past->past_InputDeviceID >= 0) && (past->past_NumInputChannels > 0) ) + { + pad = Pa_GetInternalDevice( past->past_InputDeviceID ); + hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL ); + /* If this fails, then try each input device until we find one that works. */ + if( hr != DS_OK ) + { + int i; + ERR_RPT(("Creation of requested Audio Capture device '%s' failed.\n", + ((pad->pad_lpGUID == NULL) ? "Default" : pad->pad_Info.name) )); + for( i=0; ipad_Info.maxInputChannels >= past->past_NumInputChannels ) + { + PRINT(("Try device '%s' instead.\n", pad->pad_Info.name )); + hr = DirectSoundCaptureCreate( pad->pad_lpGUID, &dsw->dsw_pDirectSoundCapture, NULL ); + if( hr == DS_OK ) break; + } + } + } + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n")); + result = paHostError; + sPaHostError = hr; + goto error; + } + hr = DSW_InitInputBuffer( dsw, + (unsigned long) (past->past_SampleRate + 0.5), + past->past_NumInputChannels, numBytes ); + DBUG(("DSW_InitInputBuffer() returns %x\n", hr)); + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr)); + result = paHostError; + sPaHostError = hr; + goto error; + } + } +#endif /* SUPPORT_AUDIO_CAPTURE */ + /* Calculate scalar used in CPULoad calculation. */ + { + LARGE_INTEGER frequency; + if( QueryPerformanceFrequency( &frequency ) == 0 ) + { + pahsc->pahsc_InverseTicksPerUserBuffer = 0.0; + } + else + { + pahsc->pahsc_InverseTicksPerUserBuffer = past->past_SampleRate / + ( (double)frequency.QuadPart * past->past_FramesPerUserBuffer ); + DBUG(("pahsc_InverseTicksPerUserBuffer = %g\n", pahsc->pahsc_InverseTicksPerUserBuffer )); + } + } + return result; +error: + PaHost_CloseStream( past ); + return result; +} +/*************************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + HRESULT hr; + PaHostSoundControl *pahsc; + PaError result = paNoError; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + /* Give user callback a chance to pre-fill buffer. */ + result = Pa_TimeSlice( past ); + if( result != paNoError ) return result; // FIXME - what if finished? + hr = DSW_StartOutput( &pahsc->pahsc_DSoundWrapper ); + DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + { + result = paHostError; + sPaHostError = hr; + goto error; + } +error: + return result; +} +/*************************************************************************/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + PaError result = paNoError; +#if SUPPORT_AUDIO_CAPTURE + HRESULT hr; + PaHostSoundControl *pahsc; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + hr = DSW_StartInput( &pahsc->pahsc_DSoundWrapper ); + DBUG(("Pa_StartStream: DSW_StartInput returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + { + result = paHostError; + sPaHostError = hr; + goto error; + } +error: +#endif /* SUPPORT_AUDIO_CAPTURE */ + return result; +} +/*************************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + PaError result = paNoError; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + past->past_StopNow = 0; + past->past_StopSoon = 0; + past->past_IsActive = 1; + /* Create timer that will wake us up so we can fill the DSound buffer. */ + { + int msecPerBuffer; + int resolution; + int bufsPerInterrupt = past->past_NumUserBuffers/4; + if( bufsPerInterrupt < 1 ) bufsPerInterrupt = 1; + msecPerBuffer = 1000 * (bufsPerInterrupt * past->past_FramesPerUserBuffer) / (int) past->past_SampleRate; + if( msecPerBuffer < 10 ) msecPerBuffer = 10; + else if( msecPerBuffer > 100 ) msecPerBuffer = 100; + resolution = msecPerBuffer/4; + pahsc->pahsc_TimerID = timeSetEvent( msecPerBuffer, resolution, (LPTIMECALLBACK) Pa_TimerCallback, + (DWORD) past, TIME_PERIODIC ); + } + if( pahsc->pahsc_TimerID == 0 ) + { + past->past_IsActive = 0; + result = paHostError; + sPaHostError = 0; + goto error; + } +error: + return result; +} +/*************************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + int timeoutMsec; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + if( abort ) past->past_StopNow = 1; + past->past_StopSoon = 1; + /* Set timeout at 20% beyond maximum time we might wait. */ + timeoutMsec = (int) (1200.0 * pahsc->pahsc_FramesPerDSBuffer / past->past_SampleRate); + while( past->past_IsActive && (timeoutMsec > 0) ) + { + Sleep(10); + timeoutMsec -= 10; + } + if( pahsc->pahsc_TimerID != 0 ) + { + timeKillEvent(pahsc->pahsc_TimerID); /* Stop callback timer. */ + pahsc->pahsc_TimerID = 0; + } + return paNoError; +} +/*************************************************************************/ +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) +{ +#if SUPPORT_AUDIO_CAPTURE + HRESULT hr; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + (void) abort; + hr = DSW_StopInput( &pahsc->pahsc_DSoundWrapper ); + DBUG(("DSW_StopInput() result is %x\n", hr)); +#endif /* SUPPORT_AUDIO_CAPTURE */ + return paNoError; +} +/*************************************************************************/ +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +{ + HRESULT hr; + PaHostSoundControl *pahsc; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + (void) abort; + hr = DSW_StopOutput( &pahsc->pahsc_DSoundWrapper ); + DBUG(("DSW_StopOutput() result is %x\n", hr)); + return paNoError; +} +/*******************************************************************/ +PaError PaHost_CloseStream( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + DSW_Term( &pahsc->pahsc_DSoundWrapper ); + if( pahsc->pahsc_NativeBuffer ) + { + PaHost_FreeFastMemory( pahsc->pahsc_NativeBuffer, pahsc->pahsc_BytesPerBuffer ); /* MEM */ + pahsc->pahsc_NativeBuffer = NULL; + } + PaHost_FreeFastMemory( pahsc, sizeof(PaHostSoundControl) ); /* MEM */ + past->past_DeviceData = NULL; + return paNoError; +} + +/* Set minimal latency based on whether NT or Win95. + * NT has higher latency. + */ +static int PaHost_GetMinSystemLatency( void ) +{ + int minLatencyMsec; + /* Set minimal latency based on whether NT or other OS. + * NT has higher latency. + */ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof( osvi ); + GetVersionEx( &osvi ); + DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId )); + DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion )); + DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion )); + /* Check for NT */ + if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) + { + minLatencyMsec = PA_WIN_NT_LATENCY; + } + else if(osvi.dwMajorVersion >= 5) + { + minLatencyMsec = PA_WIN_WDM_LATENCY; + } + else + { + minLatencyMsec = PA_WIN_9X_LATENCY; + } + return minLatencyMsec; +} + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** +** set PA_MIN_LATENCY_MSEC=200 +** +** in the AUTOEXEC.BAT file and reboot. +** If the environment variable is not set, then the latency will be determined +** based on the OS. Windows NT has higher latency than Win95. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") +int Pa_GetMinNumBuffers( int framesPerBuffer, double sampleRate ) +{ + char envbuf[PA_ENV_BUF_SIZE]; + DWORD hresult; + int minLatencyMsec = 0; + double msecPerBuffer = (1000.0 * framesPerBuffer) / sampleRate; + int minBuffers; + /* Let user determine minimal latency by setting environment variable. */ + hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + minLatencyMsec = atoi( envbuf ); + } + else + { + minLatencyMsec = PaHost_GetMinSystemLatency(); +#if PA_USE_HIGH_LATENCY + PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec )); +#endif + + } + minBuffers = (int) (1.0 + ((double)minLatencyMsec / msecPerBuffer)); + if( minBuffers < 2 ) minBuffers = 2; + return minBuffers; +} +/*************************************************************************/ +PaError PaHost_Term( void ) +{ + int i; + /* Free names allocated during enumeration. */ + for( i=0; ipast_DeviceData; + if( pahsc == NULL ) return paInternalError; + return (PaError) (past->past_IsActive); +} +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + DSoundWrapper *dsw; + internalPortAudioStream *past = (internalPortAudioStream *) stream; + PaHostSoundControl *pahsc; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + dsw = &pahsc->pahsc_DSoundWrapper; + return dsw->dsw_FramesPlayed; +} diff --git a/pd/portaudio/pa_win_ds/pa_win_ds.c b/pd/portaudio/pa_win_ds/pa_win_ds.c new file mode 100644 index 00000000..b23a407e --- /dev/null +++ b/pd/portaudio/pa_win_ds/pa_win_ds.c @@ -0,0 +1,1441 @@ +/* + * $Id: pa_win_ds.c,v 1.1.2.29 2002/12/03 06:30:40 rossbencina Exp $ + * Portable Audio I/O Library DirectSound implementation + * + * 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 + + @todo implement underflow/overflow streamCallback statusFlags, paNeverDropInput. + + @todo implement host api specific extension to set i/o buffer sizes in frames + + @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.) + + @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable + + @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into + a native portaudio error code. Standard DirectSound result codes are documented at msdn. + + @todo implement IsFormatSupported + + @todo implement PaDeviceInfo.defaultSampleRate; +*/ + +#include +#include /* strlen() */ + +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "dsound_wrapper.h" + +/* TODO +O- fix "patest_stop.c" +O- Handle buffer underflow, overflow, etc. +*/ + +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +#define PA_USE_HIGH_LATENCY (0) +#if PA_USE_HIGH_LATENCY +#define PA_WIN_9X_LATENCY (500) +#define PA_WIN_NT_LATENCY (600) +#else +#define PA_WIN_9X_LATENCY (140) +#define PA_WIN_NT_LATENCY (280) +#endif + +#define PA_WIN_WDM_LATENCY (120) + +#define SECONDS_PER_MSEC (0.001) +#define MSEC_PER_SECOND (1000) + +/* prototypes for functions declared in this file */ + +PaError PaWinDs_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, void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); + + +/* FIXME: should convert hr to a string */ +#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \ + PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" ) + +/************************************************* DX Prototypes **********/ +static BOOL CALLBACK Pa_EnumOutputProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ); +static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ); + +/************************************************************************************/ +/********************** Structures **************************************************/ +/************************************************************************************/ +/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct PaWinDsDeviceInfo +{ + GUID GUID; + GUID *lpGUID; + double sampleRates[3]; +} PaWinDsDeviceInfo; + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + /* implementation specific data goes here */ + PaWinDsDeviceInfo *winDsDeviceInfos; + PaError enumerationError; + +} PaWinDsHostApiRepresentation; + +/* PaWinDsStream - a stream data structure specifically for this implementation */ + +typedef struct PaWinDsStream +{ + PaUtilStreamRepresentation streamRepresentation; + PaUtilCpuLoadMeasurer cpuLoadMeasurer; + PaUtilBufferProcessor bufferProcessor; + +/* DirectSound specific data. */ + DSoundWrapper directSoundWrapper; + MMRESULT timerID; + BOOL ifInsideCallback; /* Test for reentrancy. */ + int framesPerDSBuffer; + double framesWritten; + double secondsPerHostByte; /* Used to optimize latency calculation for outTime */ + +/* FIXME - move all below to PaUtilStreamRepresentation */ + volatile int isStarted; + volatile int isActive; + volatile int stopProcessing; /* stop thread once existing buffers have been returned */ + volatile int abortProcessing; /* stop thread immediately */ +} PaWinDsStream; + + +/************************************************************************************ +** Just count devices so we know how much memory to allocate. +*/ +static BOOL CALLBACK Pa_CountDevProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + int *counterPtr = (int *)lpContext; + *counterPtr += 1; + return TRUE; +} + +/************************************************************************************ +** Extract capabilities info from each device. +*/ +static BOOL CALLBACK Pa_EnumOutputProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + HRESULT hr; + DSCAPS caps; + LPDIRECTSOUND lpDirectSound; + PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation *) lpContext; + PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep; + int index = hostApi->info.deviceCount; + PaDeviceInfo *deviceInfo = hostApi->deviceInfos[index]; + PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[index]; + int deviceOK = TRUE; + + /* Copy GUID to static array. Set pointer. */ + if( lpGUID == NULL ) + { + winDsDeviceInfo->lpGUID = NULL; + } + else + { + winDsDeviceInfo->lpGUID = &winDsDeviceInfo->GUID; + memcpy( &winDsDeviceInfo->GUID, lpGUID, sizeof(GUID) ); + } + + + /********** Output ******************************/ + + /* Create interfaces for each object. */ + hr = dswDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL ); + if( hr != DS_OK ) + { + deviceInfo->maxOutputChannels = 0; + DBUG(("Cannot create dsound for %s. Result = 0x%x\n", lpszDesc, hr )); + deviceOK = FALSE; + } + else + { + /* Query device characteristics. */ + caps.dwSize = sizeof(caps); + IDirectSound_GetCaps( lpDirectSound, &caps ); + +#ifndef PA_NO_WMME + if( caps.dwFlags & DSCAPS_EMULDRIVER ) + { + /* If WMME supported, then reject Emulated drivers because they are lousy. */ + deviceOK = FALSE; + } +#endif + + if( deviceOK ) + { + /* Mono or stereo device? */ + deviceInfo->maxOutputChannels = ( caps.dwFlags & DSCAPS_PRIMARYSTEREO ) ? 2 : 1; + + deviceInfo->defaultSampleRate = 0.; /* @todo IMPLEMENT ME */ + + /* Get sample rates. */ + /* + winDsDeviceInfo->sampleRates[0] = (double) caps.dwMinSecondarySampleRate; + winDsDeviceInfo->sampleRates[1] = (double) caps.dwMaxSecondarySampleRate; + if( caps.dwFlags & DSCAPS_CONTINUOUSRATE ) deviceInfo->numSampleRates = -1; + else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate ) + { + if( caps.dwMinSecondarySampleRate == 0 ) + { + // + // On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !! + // But it supports continuous sampling. + // So fake range of rates, and hope it really supports it. + // + winDsDeviceInfo->sampleRates[0] = 11025.0f; + winDsDeviceInfo->sampleRates[1] = 48000.0f; + deviceInfo->numSampleRates = -1; // continuous range + + DBUG(("PA - Reported rates both zero. Setting to fake values for device #%d\n", sDeviceIndex )); + } + else + { + deviceInfo->numSampleRates = 1; + } + } + else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) ) + { + // The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000. + // But we know that they really support a range of rates! + // So when we see a ridiculous set of rates, assume it is a range. + // + deviceInfo->numSampleRates = -1; + DBUG(("PA - Sample rate range used instead of two odd values for device #%d\n", sDeviceIndex )); + } + else deviceInfo->numSampleRates = 2; + */ + } + + IDirectSound_Release( lpDirectSound ); + } + + if( deviceOK ) + { + if( lpGUID == NULL ) hostApi->info.defaultOutputDevice = index; + + /* Allocate room for descriptive name. */ + if( lpszDesc != NULL ) + { + char *deviceName; + int len = strlen(lpszDesc); + deviceName = (char*)PaUtil_GroupAllocateMemory( winDsHostApi->allocations, len + 1 ); + if( !deviceName ) + { + winDsHostApi->enumerationError = paInsufficientMemory; + return FALSE; + } + memcpy( (void *) deviceName, lpszDesc, len+1 ); + deviceInfo->name = deviceName; + } + + hostApi->info.deviceCount++; + } + + return( TRUE ); +} + + +/************************************************************************************ +** Extract capabilities info from each device. +*/ +static BOOL CALLBACK Pa_EnumInputProc(LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + HRESULT hr; + DSCCAPS caps; + LPDIRECTSOUNDCAPTURE lpDirectSoundCapture; + PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation *) lpContext; + PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep; + int index = hostApi->info.deviceCount; + PaDeviceInfo *deviceInfo = hostApi->deviceInfos[index]; + PaWinDsDeviceInfo *winDsDeviceInfo = &winDsHostApi->winDsDeviceInfos[index]; + int deviceOK = TRUE; + + /* Copy GUID to static array. Set pointer. */ + if( lpGUID == NULL ) + { + winDsDeviceInfo->lpGUID = NULL; + } + else + { + winDsDeviceInfo->lpGUID = &winDsDeviceInfo->GUID; + memcpy( &winDsDeviceInfo->GUID, lpGUID, sizeof(GUID) ); + } + + /********** Input ******************************/ + + hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL ); + if( hr != DS_OK ) + { + deviceInfo->maxInputChannels = 0; + DBUG(("Cannot create Capture for %s. Result = 0x%x\n", lpszDesc, hr )); + deviceOK = FALSE; + } + else + { + /* Query device characteristics. */ + caps.dwSize = sizeof(caps); + IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps ); + + /* printf("caps.dwFormats = 0x%x\n", caps.dwFormats ); */ + deviceInfo->maxInputChannels = caps.dwChannels; + /* Determine sample rates from flags. */ + /* + if( caps.dwChannels == 2 ) + { + int index = 0; + if( caps.dwFormats & WAVE_FORMAT_1S16) winDsDeviceInfo->sampleRates[index++] = 11025.0; + if( caps.dwFormats & WAVE_FORMAT_2S16) winDsDeviceInfo->sampleRates[index++] = 22050.0; + if( caps.dwFormats & WAVE_FORMAT_4S16) winDsDeviceInfo->sampleRates[index++] = 44100.0; + deviceInfo->numSampleRates = index; + } + else if( caps.dwChannels == 1 ) + { + int index = 0; + if( caps.dwFormats & WAVE_FORMAT_1M16) winDsDeviceInfo->sampleRates[index++] = 11025.0; + if( caps.dwFormats & WAVE_FORMAT_2M16) winDsDeviceInfo->sampleRates[index++] = 22050.0; + if( caps.dwFormats & WAVE_FORMAT_4M16) winDsDeviceInfo->sampleRates[index++] = 44100.0; + deviceInfo->numSampleRates = index; + } + else + { + deviceInfo->numSampleRates = 0; + deviceOK = FALSE; + } + */ + IDirectSoundCapture_Release( lpDirectSoundCapture ); + } + + if( deviceOK ) + { + /* Allocate room for descriptive name. */ + if( lpszDesc != NULL ) + { + char *deviceName; + int len = strlen(lpszDesc); + deviceName = (char*)PaUtil_GroupAllocateMemory( winDsHostApi->allocations, len + 1 ); + if( !deviceName ) + { + winDsHostApi->enumerationError = paInsufficientMemory; + return FALSE; + } + memcpy( (void *) deviceName, lpszDesc, len+1 ); + deviceInfo->name = deviceName; + } + + if( lpGUID == NULL ) hostApi->info.defaultInputDevice = index; + + hostApi->info.deviceCount++; + } + + return TRUE; +} + + +/***********************************************************************************/ +PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + int i, deviceCount; + PaWinDsHostApiRepresentation *winDsHostApi; + PaDeviceInfo *deviceInfoArray; + + DSW_InitializeDSoundEntryPoints(); + + winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) ); + if( !winDsHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + winDsHostApi->allocations = PaUtil_CreateAllocationGroup(); + if( !winDsHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + *hostApi = &winDsHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paDirectSound; + (*hostApi)->info.name = "Windows DirectSound"; + + (*hostApi)->info.deviceCount = 0; + (*hostApi)->info.defaultInputDevice = paNoDevice; + (*hostApi)->info.defaultOutputDevice = paNoDevice; + + deviceCount = 0; +/* DSound - enumerate devices to count them. */ + dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, &deviceCount ); + dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_CountDevProc, &deviceCount ); + + if( deviceCount > 0 ) + { + /* allocate array for pointers to PaDeviceInfo structs */ + (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount ); + if( !(*hostApi)->deviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all PaDeviceInfo structs in a contiguous block */ + deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( + winDsHostApi->allocations, sizeof(PaDeviceInfo) * deviceCount ); + if( !deviceInfoArray ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all DSound specific info structs in a contiguous block */ + winDsHostApi->winDsDeviceInfos = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory( + winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount ); + if( !winDsHostApi->winDsDeviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + for( i=0; i < deviceCount; ++i ) + { + PaDeviceInfo *deviceInfo = &deviceInfoArray[i]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + deviceInfo->name = 0; + + deviceInfo->defaultLowInputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultLowOutputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultHighInputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultHighOutputLatency = 0.; /* @todo IMPLEMENT ME */ + + (*hostApi)->deviceInfos[i] = deviceInfo; + } + + /* DSound - Enumerate again to fill in structures. */ + winDsHostApi->enumerationError = 0; + dswDSoundEntryPoints.DirectSoundEnumerate( (LPDSENUMCALLBACK)Pa_EnumOutputProc, (void *)winDsHostApi ); + if( winDsHostApi->enumerationError != paNoError ) return winDsHostApi->enumerationError; + + winDsHostApi->enumerationError = 0; + dswDSoundEntryPoints.DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)Pa_EnumInputProc, (void *)winDsHostApi ); + if( winDsHostApi->enumerationError != paNoError ) return winDsHostApi->enumerationError; + } + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyReadWrite, PaUtil_DummyReadWrite, PaUtil_DummyGetAvailable, PaUtil_DummyGetAvailable ); + + PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + return result; + +error: + if( winDsHostApi ) + { + if( winDsHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winDsHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); + } + + PaUtil_FreeMemory( winDsHostApi ); + } + return result; +} + + +/***********************************************************************************/ +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; + + /* + IMPLEMENT ME: + - clean up any resourced not handled by the allocation group + */ + + if( winDsHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winDsHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winDsHostApi->allocations ); + } + + PaUtil_FreeMemory( winDsHostApi ); + + DSW_TerminateDSoundEntryPoints(); +} + + +/* Set minimal latency based on whether NT or Win95. + * NT has higher latency. + */ +static int PaWinDS_GetMinSystemLatency( void ) +{ + int minLatencyMsec; + /* Set minimal latency based on whether NT or other OS. + * NT has higher latency. + */ + OSVERSIONINFO osvi; + osvi.dwOSVersionInfoSize = sizeof( osvi ); + GetVersionEx( &osvi ); + DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId )); + DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion )); + DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion )); + /* Check for NT */ + if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) ) + { + minLatencyMsec = PA_WIN_NT_LATENCY; + } + else if(osvi.dwMajorVersion >= 5) + { + minLatencyMsec = PA_WIN_WDM_LATENCY; + } + else + { + minLatencyMsec = PA_WIN_9X_LATENCY; + } + return minLatencyMsec; +} + +/***********************************************************************************/ +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + } + + /* + IMPLEMENT ME: + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + */ + + return paFormatIsSupported; +} + + +/************************************************************************* +** Determine minimum number of buffers required for this host based +** on minimum latency. Latency can be optionally set by user by setting +** an environment variable. For example, to set latency to 200 msec, put: +** +** set PA_MIN_LATENCY_MSEC=200 +** +** in the AUTOEXEC.BAT file and reboot. +** If the environment variable is not set, then the latency will be determined +** based on the OS. Windows NT has higher latency than Win95. +*/ +#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC") +#define PA_ENV_BUF_SIZE (32) + +static int PaWinDs_GetMinLatencyFrames( double sampleRate ) +{ + char envbuf[PA_ENV_BUF_SIZE]; + DWORD hresult; + int minLatencyMsec = 0; + + /* Let user determine minimal latency by setting environment variable. */ + hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) ) + { + minLatencyMsec = atoi( envbuf ); + } + else + { + minLatencyMsec = PaWinDS_GetMinSystemLatency(); +#if PA_USE_HIGH_LATENCY + PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec )); +#endif + + } + + return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC); +} + +/***********************************************************************************/ +/* 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; + PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi; + PaWinDsStream *stream = 0; + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + suggestedInputLatencyFrames = inputParameters->suggestedLatency * sampleRate; + + /* IDEA: the following 3 checks could be performed by default by pa_front + unless some flag indicated otherwise */ + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate hostApiSpecificStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + suggestedInputLatencyFrames = 0; + } + + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + suggestedOutputLatencyFrames = outputParameters->suggestedLatency * sampleRate; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate hostApiSpecificStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + suggestedOutputLatencyFrames = 0; + } + + + /* + IMPLEMENT ME: + + ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() ) + + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + + - alter sampleRate to a close allowable rate if possible / necessary + + - validate suggestedInputLatency and suggestedOutputLatency parameters, + use default values where necessary + */ + + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + + stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) ); + if( !stream ) + { + result = paInsufficientMemory; + goto error; + } + + if( streamCallback ) + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &winDsHostApi->callbackStreamInterface, streamCallback, userData ); + } + else + { + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &winDsHostApi->blockingStreamInterface, streamCallback, userData ); + } + + stream->streamRepresentation.streamInfo.inputLatency = 0.; /* FIXME: not initialised anywhere else */ + stream->streamRepresentation.streamInfo.outputLatency = 0.; + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + + if( inputParameters ) + { + /* IMPLEMENT ME - establish which host formats are available */ + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputParameters->sampleFormat ); + } + + if( outputParameters ) + { + /* IMPLEMENT ME - establish which host formats are available */ + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputParameters->sampleFormat ); + } + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, hostInputSampleFormat, + outputChannelCount, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, + framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */ + /* This next mode is required because DS can split the host buffer when it wraps around. */ + paUtilVariableHostBufferSizePartialUsageAllowed, + streamCallback, userData ); + if( result != paNoError ) + goto error; + +/* DirectSound specific initialization */ + { + HRESULT hr; + int bytesPerDirectSoundBuffer; + DSoundWrapper *dsw; + int userLatencyFrames; + int minLatencyFrames; + + stream->timerID = 0; + dsw = &stream->directSoundWrapper; + DSW_Init( dsw ); + + /* Get system minimum latency. */ + minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate ); + + /* Let user override latency by passing latency parameter. */ + userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames) + ? suggestedInputLatencyFrames + : suggestedOutputLatencyFrames; + if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames; + + /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */ + if( framesPerBuffer == paFramesPerBufferUnspecified ) + { + /* App support variable framesPerBuffer */ + stream->framesPerDSBuffer = minLatencyFrames; + + stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate; + } + else + { + /* Round up to number of buffers needed to guarantee that latency. */ + int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer; + if( numUserBuffers < 1 ) numUserBuffers = 1; + numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */ + stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers; + + stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate; + } + + { + /* @todo REVIEW: this calculation seems incorrect to me - rossb. */ + int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate); + PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency )); + } + + + /* ------------------ OUTPUT */ + if( outputParameters ) + { + PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ]; + DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputDevice)); + + bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * sizeof(short); + if( bytesPerDirectSoundBuffer < DSBSIZE_MIN ) + { + result = paBufferTooSmall; + goto error; + } + else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX ) + { + result = paBufferTooBig; + goto error; + } + + + hr = dswDSoundEntryPoints.DirectSoundCreate( winDsHostApi->winDsDeviceInfos[outputParameters->device].lpGUID, + &dsw->dsw_pDirectSound, NULL ); + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n")); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + hr = DSW_InitOutputBuffer( dsw, + (unsigned long) (sampleRate + 0.5), + outputParameters->channelCount, bytesPerDirectSoundBuffer ); + DBUG(("DSW_InitOutputBuffer() returns %x\n", hr)); + if( hr != DS_OK ) + { + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + /* Calculate value used in latency calculation to avoid real-time divides. */ + stream->secondsPerHostByte = 1.0 / + (stream->bufferProcessor.bytesPerHostOutputSample * + outputChannelCount * sampleRate); + } + + /* ------------------ INPUT */ + if( inputParameters ) + { + PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ]; + + bytesPerDirectSoundBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * sizeof(short); + if( bytesPerDirectSoundBuffer < DSBSIZE_MIN ) + { + result = paBufferTooSmall; + goto error; + } + else if( bytesPerDirectSoundBuffer > DSBSIZE_MAX ) + { + result = paBufferTooBig; + goto error; + } + + hr = dswDSoundEntryPoints.DirectSoundCaptureCreate( winDsHostApi->winDsDeviceInfos[inputParameters->device].lpGUID, + &dsw->dsw_pDirectSoundCapture, NULL ); + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n")); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + hr = DSW_InitInputBuffer( dsw, + (unsigned long) (sampleRate + 0.5), + inputParameters->channelCount, bytesPerDirectSoundBuffer ); + DBUG(("DSW_InitInputBuffer() returns %x\n", hr)); + if( hr != DS_OK ) + { + ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr)); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + } + + } + + *s = (PaStream*)stream; + + return result; + +error: + if( stream ) + PaUtil_FreeMemory( stream ); + + return result; +} + + +/***********************************************************************************/ +static PaError Pa_TimeSlice( PaWinDsStream *stream ) +{ + PaError result = 0; /* FIXME: this should be declared int and this function should also return that type (same as stream callback return type)*/ + DSoundWrapper *dsw; + long numFrames = 0; + long bytesEmpty = 0; + long bytesFilled = 0; + long bytesToXfer = 0; + long framesToXfer = 0; + long numInFramesReady = 0; + long numOutFramesReady = 0; + long bytesProcessed; + HRESULT hresult; + double outputLatency = 0; + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* @todo implement inputBufferAdcTime */ +/* Input */ + LPBYTE lpInBuf1 = NULL; + LPBYTE lpInBuf2 = NULL; + DWORD dwInSize1 = 0; + DWORD dwInSize2 = 0; +/* Output */ + LPBYTE lpOutBuf1 = NULL; + LPBYTE lpOutBuf2 = NULL; + DWORD dwOutSize1 = 0; + DWORD dwOutSize2 = 0; + + dsw = &stream->directSoundWrapper; + + /* How much input data is available? */ + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + DSW_QueryInputFilled( dsw, &bytesFilled ); + framesToXfer = numInFramesReady = bytesFilled / dsw->dsw_BytesPerInputFrame; + outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte; + } + + /* How much output room is available? */ + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + DSW_QueryOutputSpace( dsw, &bytesEmpty ); + framesToXfer = numOutFramesReady = bytesEmpty / dsw->dsw_BytesPerOutputFrame; + } + + if( (numInFramesReady > 0) && (numOutFramesReady > 0) ) + { + framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady; + } + + if( framesToXfer > 0 ) + { + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + /* The outputBufferDacTime parameter should indicates the time at which + the first sample of the output buffer is heard at the DACs. */ + timeInfo.currentTime = PaUtil_GetTime(); + timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; + + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo ); + + /* Input */ + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + bytesToXfer = framesToXfer * dsw->dsw_BytesPerInputFrame; + hresult = IDirectSoundCaptureBuffer_Lock ( dsw->dsw_InputBuffer, + dsw->dsw_ReadOffset, bytesToXfer, + (void **) &lpInBuf1, &dwInSize1, + (void **) &lpInBuf2, &dwInSize2, 0); + if (hresult != DS_OK) + { + ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult)); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); + goto error2; + } + + numFrames = dwInSize1 / dsw->dsw_BytesPerInputFrame; + PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames ); + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 ); + /* Is input split into two regions. */ + if( dwInSize2 > 0 ) + { + numFrames = dwInSize2 / dsw->dsw_BytesPerInputFrame; + PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames ); + PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 ); + } + } + + /* Output */ + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + bytesToXfer = framesToXfer * dsw->dsw_BytesPerOutputFrame; + hresult = IDirectSoundBuffer_Lock ( dsw->dsw_OutputBuffer, + dsw->dsw_WriteOffset, bytesToXfer, + (void **) &lpOutBuf1, &dwOutSize1, + (void **) &lpOutBuf2, &dwOutSize2, 0); + if (hresult != DS_OK) + { + ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult)); + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); + goto error1; + } + + numFrames = dwOutSize1 / dsw->dsw_BytesPerOutputFrame; + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames ); + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 ); + + /* Is output split into two regions. */ + if( dwOutSize2 > 0 ) + { + numFrames = dwOutSize2 / dsw->dsw_BytesPerOutputFrame; + PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames ); + PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 ); + } + } + + numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &result ); + stream->framesWritten += numFrames; + + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + /* Update our buffer offset and unlock sound buffer */ + bytesProcessed = numFrames * dsw->dsw_BytesPerOutputFrame; + dsw->dsw_WriteOffset = (dsw->dsw_WriteOffset + bytesProcessed) % dsw->dsw_OutputSize; + IDirectSoundBuffer_Unlock( dsw->dsw_OutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2); + dsw->dsw_FramesWritten += numFrames; + } + +error1: + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + /* Update our buffer offset and unlock sound buffer */ + bytesProcessed = numFrames * dsw->dsw_BytesPerInputFrame; + dsw->dsw_ReadOffset = (dsw->dsw_ReadOffset + bytesProcessed) % dsw->dsw_InputSize; + IDirectSoundCaptureBuffer_Unlock( dsw->dsw_InputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2); + } +error2: + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames ); + + } + + return result; +} +/*******************************************************************/ +static void CALLBACK Pa_TimerCallback(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + PaWinDsStream *stream; + + stream = (PaWinDsStream *) dwUser; + if( stream == NULL ) return; + + if( stream->isActive ) + { + if( stream->abortProcessing ) + { + stream->isActive = 0; + } + else if( stream->stopProcessing ) + { + DSoundWrapper *dsw = &stream->directSoundWrapper; + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + DSW_ZeroEmptySpace( dsw ); + /* clear isActive when all sound played */ + if( dsw->dsw_FramesPlayed >= stream->framesWritten ) + { + stream->isActive = 0; + } + } + else + { + stream->isActive = 0; + } + } + else + { + if( Pa_TimeSlice( stream ) != 0) /* Call time slice independant of timing method. */ + { + /* FIXME implement handling of paComplete and paAbort if possible */ + stream->stopProcessing = 1; + } + } + + if( !stream->isActive ){ + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + } + } +} + +/*********************************************************************************** + When CloseStream() is called, the multi-api layer ensures that + the stream has already been stopped or aborted. +*/ +static PaError CloseStream( PaStream* s ) +{ + PaError result = paNoError; + PaWinDsStream *stream = (PaWinDsStream*)s; + + DSW_Term( &stream->directSoundWrapper ); + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + + return result; +} + +/***********************************************************************************/ +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinDsStream *stream = (PaWinDsStream*)s; + HRESULT hr; + + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + hr = DSW_StartInput( &stream->directSoundWrapper ); + DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + { + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + } + + stream->framesWritten = 0; + + stream->abortProcessing = 0; + stream->stopProcessing = 0; + stream->isActive = 1; + + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */ + result = Pa_TimeSlice( stream ); + if( result != paNoError ) return result; // FIXME - what if finished? + + hr = DSW_StartOutput( &stream->directSoundWrapper ); + DBUG(("PaHost_StartOutput: DSW_StartOutput returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + { + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + } + + + /* Create timer that will wake us up so we can fill the DSound buffer. */ + { + int resolution; + int framesPerWakeup = stream->framesPerDSBuffer / 4; + int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate; + if( msecPerWakeup < 10 ) msecPerWakeup = 10; + else if( msecPerWakeup > 100 ) msecPerWakeup = 100; + resolution = msecPerWakeup/4; + stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) Pa_TimerCallback, + (DWORD) stream, TIME_PERIODIC ); + } + if( stream->timerID == 0 ) + { + stream->isActive = 0; + result = paUnanticipatedHostError; + PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ); + goto error; + } + + stream->isStarted = TRUE; + +error: + return result; +} + + +/***********************************************************************************/ +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinDsStream *stream = (PaWinDsStream*)s; + HRESULT hr; + int timeoutMsec; + + stream->stopProcessing = 1; + /* Set timeout at 20% beyond maximum time we might wait. */ + timeoutMsec = (int) (1200.0 * stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate); + while( stream->isActive && (timeoutMsec > 0) ) + { + Sleep(10); + timeoutMsec -= 10; + } + if( stream->timerID != 0 ) + { + timeKillEvent(stream->timerID); /* Stop callback timer. */ + stream->timerID = 0; + } + + + if( stream->bufferProcessor.outputChannelCount > 0 ) + { + hr = DSW_StopOutput( &stream->directSoundWrapper ); + } + + if( stream->bufferProcessor.inputChannelCount > 0 ) + { + hr = DSW_StopInput( &stream->directSoundWrapper ); + } + + stream->isStarted = FALSE; + + return result; +} + + +/***********************************************************************************/ +static PaError AbortStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinDsStream *stream = (PaWinDsStream*)s; + + stream->abortProcessing = 1; + return StopStream( s ); +} + + +/***********************************************************************************/ +static PaError IsStreamStopped( PaStream *s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + return !stream->isStarted; +} + + +/***********************************************************************************/ +static PaError IsStreamActive( PaStream *s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + return stream->isActive; +} + +/***********************************************************************************/ +static PaTime GetStreamTime( PaStream *s ) +{ +/* + new behavior for GetStreamTime is to return a stream based seconds clock + used for the outTime parameter to the callback. + FIXME: delete this comment when the other unnecessary related code has + been cleaned from this file. + + PaWinDsStream *stream = (PaWinDsStream*)s; + DSoundWrapper *dsw; + dsw = &stream->directSoundWrapper; + return dsw->dsw_FramesPlayed; +*/ + return PaUtil_GetTime(); +} + + +/***********************************************************************************/ +static double GetStreamCpuLoad( PaStream* s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)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 ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return paNoError; +} + + +/***********************************************************************************/ +static PaError WriteStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return paNoError; +} + + +/***********************************************************************************/ +static signed long GetStreamReadAvailable( PaStream* s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + +/***********************************************************************************/ +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaWinDsStream *stream = (PaWinDsStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + + return 0; +} + + + diff --git a/pd/portaudio/pa_win_ds/portaudio.def b/pd/portaudio/pa_win_ds/portaudio.def new file mode 100644 index 00000000..8012b99e --- /dev/null +++ b/pd/portaudio/pa_win_ds/portaudio.def @@ -0,0 +1,28 @@ +LIBRARY PortAudio +DESCRIPTION 'PortAudio Portable interface to audio HW' + +EXPORTS + ; Explicit exports can go here + Pa_Initialize @1 + Pa_Terminate @2 + Pa_GetHostError @3 + Pa_GetErrorText @4 + Pa_CountDevices @5 + Pa_GetDefaultInputDeviceID @6 + Pa_GetDefaultOutputDeviceID @7 + Pa_GetDeviceInfo @8 + Pa_OpenStream @9 + Pa_OpenDefaultStream @10 + Pa_CloseStream @11 + Pa_StartStream @12 + Pa_StopStream @13 + Pa_StreamActive @14 + Pa_StreamTime @15 + Pa_GetCPULoad @16 + Pa_GetMinNumBuffers @17 + Pa_Sleep @18 + + ;123456789012345678901234567890123456 + ;000000000111111111122222222223333333 + + diff --git a/pd/portaudio/pa_win_wmme/Makefile.cygwin b/pd/portaudio/pa_win_wmme/Makefile.cygwin new file mode 100644 index 00000000..5cb4acef --- /dev/null +++ b/pd/portaudio/pa_win_wmme/Makefile.cygwin @@ -0,0 +1,34 @@ + +# Makefile for PortAudio on cygwin +# Contributed by Bill Eldridge on 6/13/2001 + +ARCH= pa_win_wmme + +TESTS:= $(wildcard pa_tests/pa*.c pa_tests/debug*.c) + +.c.o: + -gcc -c -I./pa_common $< -o $*.o + -gcc $*.o -o $*.exe -L/usr/local/lib -L$(ARCH) -lportaudio.dll -lwinmm + +all: sharedlib tests + +sharedlib: ./pa_common/pa_lib.c + gcc -c -I./pa_common pa_common/pa_lib.c -o pa_common/pa_lib.o + gcc -c -I./pa_common pa_win_wmme/pa_win_wmme.c -o pa_win_wmme/pa_win_wmme.o + dlltool --export-all --output-def pa_win_wmme/pa_lib.def pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o + gcc -shared -Wl,--enable-auto-image-base -o pa_win_wmme/portaudio.dll -Wl,--out-implib=pa_win_wmme/libportaudio.dll.a pa_win_wmme/pa_lib.def pa_common/pa_lib.o pa_win_wmme/pa_win_wmme.o -L/usr/lib/w32api -lwinmm + cp pa_win_wmme/portaudio.dll /usr/local/bin + +tests: $(TESTS:.c=.o) + +sine: + gcc -c -I./pa_common pa_tests/patest_sine.c -o pa_tests/patest_sine.o + gcc pa_tests/patest_sine.o -o pa_tests/patest_sine.exe -L/usr/local/lib -lportaudio.dll -lwinmm + +clean: + -rm ./pa_tests/*.exe + +nothing: + gcc pa_tests/patest_sine.o -L/usr/lib/w32api -L./pa_win_wmme -lportaudio.dll -lwinmm + + diff --git a/pd/portaudio/pa_win_wmme/pa_win_wmme.c b/pd/portaudio/pa_win_wmme/pa_win_wmme.c new file mode 100644 index 00000000..2b1cc57d --- /dev/null +++ b/pd/portaudio/pa_win_wmme/pa_win_wmme.c @@ -0,0 +1,2672 @@ +/* + * $Id: pa_win_wmme.c,v 1.6.2.44 2003/03/18 14:27:58 rossbencina Exp $ + * pa_win_wmme.c + * Implementation of PortAudio for Windows MultiMedia Extensions (WMME) + * + * PortAudio Portable Real-Time Audio Library + * Latest Version at: http://www.portaudio.com + * + * Authors: Ross Bencina and Phil Burk + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* Modification History: + PLB = Phil Burk + JM = Julien Maillard + RDB = Ross Bencina + PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer) + PLB20010413 - check for excessive numbers of channels + PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC + including conditional inclusion of memory.h, + and explicit typecasting on memory allocation + PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory + PLB20010816 - pass process instead of thread to SetPriorityClass() + PLB20010927 - use number of frames instead of real-time for CPULoad calculation. + JM20020118 - prevent hung thread when buffers underflow. + PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount + RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init + RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices + refactoring, renaming and fixed a few edge case bugs + RDB20020531 - converted to V19 framework + ** NOTE maintanance history is now stored in CVS ** +*/ + +/** @file + + @todo Handle case where user supplied full duplex buffer sizes are not compatible + (must be common multiples) + + @todo Fix buffer catch up code, can sometimes get stuck + + @todo Implement "close sample rate matching" if needed - is this really needed + in mme? + + @todo Investigate supporting host buffer formats > 16 bits + + @todo Implement buffer size and number of buffers code, + this code should generate defaults the way the old code did + + @todo implement underflow/overflow streamCallback statusFlags, paNeverDropInput. + + @todo Fix fixmes + + @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.) + + @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable + + @todo implement IsFormatSupported + + @todo implement PaDeviceInfo.defaultSampleRate; + + @todo define UNICODE and _UNICODE in the project settings and see what breaks +*/ + +#include +#include +#include +#include +#include +#include +#include +/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ +#ifndef __MWERKS__ +#include +#include +#endif /* __MWERKS__ */ + +#include "portaudio.h" +#include "pa_trace.h" +#include "pa_util.h" +#include "pa_allocation.h" +#include "pa_hostapi.h" +#include "pa_stream.h" +#include "pa_cpuload.h" +#include "pa_process.h" + +#include "pa_win_wmme.h" + +/************************************************* Constants ********/ + +/* Switches for debugging. */ +#define PA_SIMULATE_UNDERFLOW_ (0) /* Set to one to force an underflow of the output buffer. */ + +#define PA_USE_HIGH_LATENCY_ (0) /* For debugging glitches. */ + +#if PA_USE_HIGH_LATENCY_ + #define PA_MIN_MSEC_PER_HOST_BUFFER_ (100) + #define PA_MAX_MSEC_PER_HOST_BUFFER_ (300) /* Do not exceed unless user buffer exceeds */ + #define PA_MIN_NUM_HOST_BUFFERS_ (4) + #define PA_MAX_NUM_HOST_BUFFERS_ (16) /* OK to exceed if necessary */ + #define PA_WIN_9X_LATENCY_ (400) +#else + #define PA_MIN_MSEC_PER_HOST_BUFFER_ (10) + #define PA_MAX_MSEC_PER_HOST_BUFFER_ (100) /* Do not exceed unless user buffer exceeds */ + #define PA_MIN_NUM_HOST_BUFFERS_ (3) + #define PA_MAX_NUM_HOST_BUFFERS_ (16) /* OK to exceed if necessary */ + #define PA_WIN_9X_LATENCY_ (200) +#endif + +#define PA_MIN_TIMEOUT_MSEC_ (1000) + +/* +** Use higher latency for NT because it is even worse at real-time +** operation than Win9x. +*/ +#define PA_WIN_NT_LATENCY_ (PA_WIN_9X_LATENCY_ * 2) +#define PA_WIN_WDM_LATENCY_ (PA_WIN_9X_LATENCY_) + + +static const char constInputMapperSuffix_[] = " - Input"; +static const char constOutputMapperSuffix_[] = " - Output"; + + +typedef struct PaWinMmeStream PaWinMmeStream; /* forward reference */ + +/* prototypes for functions declared in this file */ + +PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ); +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); +static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, + PaStream** stream, + 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, void *buffer, unsigned long frames ); +static signed long GetStreamReadAvailable( PaStream* stream ); +static signed long GetStreamWriteAvailable( PaStream* stream ); + +static PaError UpdateStreamTime( PaWinMmeStream *stream ); + +/* macros for setting last host error information */ + +#ifdef UNICODE + +#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \ + { \ + wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \ + char mmeErrorText[ MAXERRORLENGTH ]; \ + waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \ + WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\ + mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \ + PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ + } + +#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \ + { \ + wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \ + char mmeErrorText[ MAXERRORLENGTH ]; \ + waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \ + WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\ + mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \ + PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ + } + +#else /* !UNICODE */ + +#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \ + { \ + char mmeErrorText[ MAXERRORLENGTH ]; \ + waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \ + PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ + } + +#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \ + { \ + char mmeErrorText[ MAXERRORLENGTH ]; \ + waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \ + PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ + } + +#endif /* UNICODE */ + + +static void PaMme_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( paMME, errorCode, lpMsgBuf ); + LocalFree( lpMsgBuf ); +} + +#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \ + PaMme_SetLastSystemError( errorCode ) + + +/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */ + +typedef struct +{ + PaUtilHostApiRepresentation inheritedHostApiRep; + PaUtilStreamInterface callbackStreamInterface; + PaUtilStreamInterface blockingStreamInterface; + + PaUtilAllocationGroup *allocations; + + int numInputDevices, numOutputDevices; + + /** winMmeDeviceIds is an array of WinMme device ids. + fields in the range [0, numInputDevices) are input device ids, + and [numInputDevices, numInputDevices + numOutputDevices) are output + device ids. + */ + int *winMmeDeviceIds; +} +PaWinMmeHostApiRepresentation; + + +/************************************************************************* + * Returns recommended device ID. + * On the PC, the recommended device can be specified by the user by + * setting an environment variable. For example, to use device #1. + * + * set PA_RECOMMENDED_OUTPUT_DEVICE=1 + * + * The user should first determine the available device ID by using + * the supplied application "pa_devs". + */ +#define PA_ENV_BUF_SIZE_ (32) +#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE") +#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE") +static PaDeviceIndex GetEnvDefaultDeviceID( char *envName ) +{ + PaDeviceIndex recommendedIndex = paNoDevice; + DWORD hresult; + char envbuf[PA_ENV_BUF_SIZE_]; + +#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */ + + /* Let user determine default device by setting environment variable. */ + hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ ); + if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) ) + { + recommendedIndex = atoi( envbuf ); + } +#endif + + return recommendedIndex; +} + +static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi ) +{ + PaDeviceIndex device; + + /* input */ + device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ ); + if( device != paNoDevice && + ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) && + hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 ) + { + hostApi->inheritedHostApiRep.info.defaultInputDevice = device; + } + + /* output */ + device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ ); + if( device != paNoDevice && + ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) && + hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 ) + { + hostApi->inheritedHostApiRep.info.defaultOutputDevice = device; + } +} + + +/** Convert external PA ID to a windows multimedia device ID +*/ +static int LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device ) +{ + assert( device >= 0 && device < hostApi->numInputDevices + hostApi->numOutputDevices ); + + return hostApi->winMmeDeviceIds[ device ]; +} + + +static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, + PaDeviceInfo *deviceInfo, int winMmeInputDeviceId, int *success ) +{ + PaError result = paNoError; + char *deviceName; /* non-const ptr */ + MMRESULT mmresult; + WAVEINCAPS wic; + + *success = 0; + + mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) ); + if( mmresult == MMSYSERR_NOMEM ) + { + result = paInsufficientMemory; + goto error; + } + else if( mmresult != MMSYSERR_NOERROR ) + { + /* instead of returning paUnanticipatedHostError we return + paNoError, but leave success set as 0. This allows + Pa_Initialize to just ignore this device, without failing + the entire initialisation process. + */ + return paNoError; + } + + if( winMmeInputDeviceId == WAVE_MAPPER ) + { + /* Append I/O suffix to WAVE_MAPPER device. */ + deviceName = (char *)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, wic.szPname ); + strcat( deviceName, constInputMapperSuffix_ ); + } + else + { + deviceName = (char*)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, strlen( wic.szPname ) + 1 ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, wic.szPname ); + } + deviceInfo->name = deviceName; + + deviceInfo->maxInputChannels = wic.wChannels; + /* Sometimes a device can return a rediculously large number of channels. + * This happened with an SBLive card on a Windows ME box. + * If that happens, then force it to 2 channels. PLB20010413 + */ + if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) ) + { + PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels )); + deviceInfo->maxInputChannels = 2; + } + + deviceInfo->defaultSampleRate = 0.; /* @todo IMPLEMENT ME */ + + *success = 1; + +error: + return result; +} + + +static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, + PaDeviceInfo *deviceInfo, int winMmeOutputDeviceId, int *success ) +{ + PaError result = paNoError; + char *deviceName; /* non-const ptr */ + MMRESULT mmresult; + WAVEOUTCAPS woc; + + *success = 0; + + mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) ); + if( mmresult == MMSYSERR_NOMEM ) + { + result = paInsufficientMemory; + goto error; + } + else if( mmresult != MMSYSERR_NOERROR ) + { + /* instead of returning paUnanticipatedHostError we return + paNoError, but leave success set as 0. This allows + Pa_Initialize to just ignore this device, without failing + the entire initialisation process. + */ + return paNoError; + } + + if( winMmeOutputDeviceId == WAVE_MAPPER ) + { + /* Append I/O suffix to WAVE_MAPPER device. */ + deviceName = (char *)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, woc.szPname ); + strcat( deviceName, constOutputMapperSuffix_ ); + } + else + { + deviceName = (char*)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, strlen( woc.szPname ) + 1 ); + if( !deviceName ) + { + result = paInsufficientMemory; + goto error; + } + strcpy( deviceName, woc.szPname ); + } + deviceInfo->name = deviceName; + + deviceInfo->maxOutputChannels = woc.wChannels; + /* Sometimes a device can return a rediculously large number of channels. + * This happened with an SBLive card on a Windows ME box. + * It also happens on Win XP! + */ + if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) ) + { + PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels )); + deviceInfo->maxOutputChannels = 2; + } + + deviceInfo->defaultSampleRate = 0.; /* @todo IMPLEMENT ME */ + + *success = 1; + +error: + return result; +} + + +PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) +{ + PaError result = paNoError; + int i; + PaWinMmeHostApiRepresentation *winMmeHostApi; + int numInputDevices, numOutputDevices, maximumPossibleNumDevices; + PaDeviceInfo *deviceInfoArray; + int deviceInfoInitializationSucceeded; + + winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) ); + if( !winMmeHostApi ) + { + result = paInsufficientMemory; + goto error; + } + + winMmeHostApi->allocations = PaUtil_CreateAllocationGroup(); + if( !winMmeHostApi->allocations ) + { + result = paInsufficientMemory; + goto error; + } + + *hostApi = &winMmeHostApi->inheritedHostApiRep; + (*hostApi)->info.structVersion = 1; + (*hostApi)->info.type = paMME; + (*hostApi)->info.name = "MME"; + + + /* initialise device counts and default devices under the assumption that + there are no devices. These values are incremented below if and when + devices are successfully initialized. + */ + (*hostApi)->info.deviceCount = 0; + (*hostApi)->info.defaultInputDevice = paNoDevice; + (*hostApi)->info.defaultOutputDevice = paNoDevice; + winMmeHostApi->numInputDevices = 0; + winMmeHostApi->numOutputDevices = 0; + + + maximumPossibleNumDevices = 0; + + numInputDevices = waveInGetNumDevs(); + if( numInputDevices > 0 ) + maximumPossibleNumDevices += numInputDevices + 1; /* assume there is a WAVE_MAPPER */ + + numOutputDevices = waveOutGetNumDevs(); + if( numOutputDevices > 0 ) + maximumPossibleNumDevices += numOutputDevices + 1; /* assume there is a WAVE_MAPPER */ + + + if( maximumPossibleNumDevices > 0 ){ + + (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleNumDevices ); + if( !(*hostApi)->deviceInfos ) + { + result = paInsufficientMemory; + goto error; + } + + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, sizeof(PaDeviceInfo) * maximumPossibleNumDevices ); + if( !deviceInfoArray ) + { + result = paInsufficientMemory; + goto error; + } + + winMmeHostApi->winMmeDeviceIds = (int*)PaUtil_GroupAllocateMemory( + winMmeHostApi->allocations, sizeof(int) * maximumPossibleNumDevices ); + if( !winMmeHostApi->winMmeDeviceIds ) + { + result = paInsufficientMemory; + goto error; + } + + if( numInputDevices > 0 ){ + // -1 is the WAVE_MAPPER + for( i = -1; i < numInputDevices; ++i ){ + PaDeviceInfo *deviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + + deviceInfo->defaultLowInputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultLowOutputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultHighInputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultHighOutputLatency = 0.; /* @todo IMPLEMENT ME */ + + result = InitializeInputDeviceInfo( winMmeHostApi, deviceInfo, i, &deviceInfoInitializationSucceeded ); + if( result != paNoError ) + goto error; + + if( deviceInfoInitializationSucceeded ){ + if( (*hostApi)->info.defaultInputDevice == paNoDevice ) + (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; + + winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = i; + (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; + + winMmeHostApi->numInputDevices++; + (*hostApi)->info.deviceCount++; + } + } + } + + if( numOutputDevices > 0 ){ + // -1 is the WAVE_MAPPER + for( i = -1; i < numOutputDevices; ++i ){ + PaDeviceInfo *deviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; + deviceInfo->structVersion = 2; + deviceInfo->hostApi = hostApiIndex; + + deviceInfo->maxInputChannels = 0; + deviceInfo->maxOutputChannels = 0; + + deviceInfo->defaultLowInputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultLowOutputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultHighInputLatency = 0.; /* @todo IMPLEMENT ME */ + deviceInfo->defaultHighOutputLatency = 0.; /* @todo IMPLEMENT ME */ + + result = InitializeOutputDeviceInfo( winMmeHostApi, deviceInfo, i, &deviceInfoInitializationSucceeded ); + if( result != paNoError ) + goto error; + + if( deviceInfoInitializationSucceeded ){ + if( (*hostApi)->info.defaultOutputDevice == paNoDevice ) + (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; + + winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = i; + (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; + + winMmeHostApi->numOutputDevices++; + (*hostApi)->info.deviceCount++; + } + } + } + } + + + InitializeDefaultDeviceIdsFromEnv( winMmeHostApi ); + + (*hostApi)->Terminate = Terminate; + (*hostApi)->OpenStream = OpenStream; + (*hostApi)->IsFormatSupported = IsFormatSupported; + + PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, GetStreamCpuLoad, + PaUtil_DummyReadWrite, PaUtil_DummyReadWrite, PaUtil_DummyGetAvailable, PaUtil_DummyGetAvailable ); + + PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream, + StopStream, AbortStream, IsStreamStopped, IsStreamActive, + GetStreamTime, PaUtil_DummyGetCpuLoad, + ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); + + return result; + +error: + if( winMmeHostApi ) + { + if( winMmeHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winMmeHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations ); + } + + PaUtil_FreeMemory( winMmeHostApi ); + } + + return result; +} + + +static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) +{ + PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; + + if( winMmeHostApi->allocations ) + { + PaUtil_FreeAllAllocations( winMmeHostApi->allocations ); + PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations ); + } + + PaUtil_FreeMemory( winMmeHostApi ); +} + + +static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, + const PaStreamParameters *inputParameters, + const PaStreamParameters *outputParameters, + double sampleRate ) +{ + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that input device can support inputChannelCount */ + if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels ) + return paInvalidChannelCount; + + /* validate inputStreamInfo */ + if( inputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + inputChannelCount = 0; + } + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + + /* unless alternate device specification is supported, reject the use of + paUseHostApiSpecificDeviceSpecification */ + + if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + /* check that output device can support inputChannelCount */ + if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* validate outputStreamInfo */ + if( outputParameters->hostApiSpecificStreamInfo ) + return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ + } + else + { + outputChannelCount = 0; + } + + /* + IMPLEMENT ME: + - check that input device can support inputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - check that output device can support outputSampleFormat, or that + we have the capability to convert from outputSampleFormat to + a native format + + - if a full duplex stream is requested, check that the combination + of input and output parameters is supported + + - check that the device supports sampleRate + */ + + return paFormatIsSupported; +} + + +/* CalculateBufferSettings() fills the framesPerHostInputBuffer, numHostInputBuffers, + framesPerHostOutputBuffer and numHostOutputBuffers parameters based on the values + of the other parameters. + +*/ + +static PaError CalculateBufferSettings( + unsigned long *framesPerHostInputBuffer, unsigned long *numHostInputBuffers, + unsigned long *framesPerHostOutputBuffer, unsigned long *numHostOutputBuffers, + int inputChannelCount, PaSampleFormat hostInputSampleFormat, + PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo, + int outputChannelCount, PaSampleFormat hostOutputSampleFormat, + PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo, + double sampleRate, unsigned long framesPerBuffer ) +{ + PaError result = paNoError; + + if( inputChannelCount > 0 ) + { + if( inputStreamInfo ) + { + if( inputStreamInfo->flags & PaWinMmeUseLowLevelLatencyParameters ) + { + if( inputStreamInfo->numBuffers <= 0 + || inputStreamInfo->framesPerBuffer <= 0 ) + { + result = paIncompatibleHostApiSpecificStreamInfo; + goto error; + } + + *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer; + *numHostInputBuffers = inputStreamInfo->numBuffers; + } + } + else + { + /* hardwire for now, FIXME */ + /* don't forget that there will be one more buffer than the number required to achieve the requested latency */ + *framesPerHostInputBuffer = 4096; + *numHostInputBuffers = 4; + + /* + Need to determine the right heuristic for mapping latency in + seconds to buffer sizes and number of buffers. + + - for output don't allocate less than 1+1 buffers + + - for input don't allocate less than 2+1 buffers + (less than 1+1 if input only) + + - don't allocate buffers smaller than framesPerBuffer + + - if the client doesn't care about the buffer size use a power + of two buffer size. otherwise use a multiple of the user + buffer size. if the user buffer size is a power of 2, it + might be wise to make the host buffer size a power of 2 too. + + - there probably shouldn't be too many buffers ( 3 to 10 seems + reasonable). + + - aside from a limit on what constitutes a "reasonable" number + of buffers, there should be as many buffers as possible, + because this will place a less bursty load on CPU resources + + - the host buffers should be as big as practical (ie multiple + user buffers per host buffer). + + + . One way to achieve the above is to say: Try to have 8 + host buffers, and host buffers cannot be larger than 32k + unless the user buffer size requires it" + I say 32k because buffers larger than this are known to + crash some drivers (Turtle Beach for example.) + + just some idle rambling: + + if( framesPerBuffer == 0 ){ + // use a power of two buffer size + + }else{ + latencySamples = ceil(requestedLatency * sampleRate) + + numBuffers = ceil(latencySamples / framesPerBuffer); + + bufferSize = framesPerBuffer; + + minBuffers = ( inputChannelCount > 0 && outputChannelCount > 0 ) ? 2 : 1; + + if( numBuffers <= minBuffers ){ + numBuffers = minBuffers; + }else{ + make buffer size a multiple of framesPerBuffer until numBuffers is + greater than 4 and less than 10. + } + } + */ + } + } + else + { + *framesPerHostInputBuffer = 0; + *numHostInputBuffers = 0; + } + + if( outputChannelCount > 0 ) + { + if( outputStreamInfo ) + { + if( outputStreamInfo->flags & PaWinMmeUseLowLevelLatencyParameters ) + { + if( outputStreamInfo->numBuffers <= 0 + || outputStreamInfo->framesPerBuffer <= 0 ) + { + result = paIncompatibleHostApiSpecificStreamInfo; + goto error; + } + + *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer; + *numHostOutputBuffers = outputStreamInfo->numBuffers; + } + } + else + { + /* hardwire for now, FIXME */ + /* don't forget that there will be one more buffer than the number required to achieve the requested latency */ + *framesPerHostOutputBuffer = 4096; + *numHostOutputBuffers = 4; + } + } + else + { + *framesPerHostOutputBuffer = 0; + *numHostOutputBuffers = 0; + } + +error: + return result; +} + + + +typedef HWAVEIN MmeHandle; + +static PaError InitializeBufferSet( WAVEHDR **bufferSet, int numBuffers, int bufferBytes, + int isInput, /* if 0, then output */ + MmeHandle mmeWaveHandle, int numDeviceChannels ) +{ + PaError result = paNoError; + MMRESULT mmresult; + int i; + + *bufferSet = 0; + + /* Allocate an array to hold the buffer pointers. */ + *bufferSet = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*numBuffers ); + if( !*bufferSet ) + { + result = paInsufficientMemory; + goto error; + } + + for( i=0; i don't throtte, non-0 -> throttle */ + int processingThreadPriority; + int highThreadPriority; + int throttledThreadPriority; + + volatile int isActive; + volatile int stopProcessing; /* stop thread once existing buffers have been returned */ + volatile int abortProcessing; /* stop thread immediately */ + + DWORD allBuffersDurationMs; /* used to calculate timeouts */ + + /* @todo FIXME: we no longer need the following for GetStreamTime support */ + /* GetStreamTime() support ------------- */ + + PaTime streamPosition; + long previousStreamPosition; /* used to track frames played. */ +} +PaWinMmeStream; + + +/* the following macros are intended to improve the readability of the following code */ +#define PA_IS_INPUT_STREAM_( stream ) ( stream ->hWaveIns ) +#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->hWaveOuts ) +#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->hWaveIns && stream ->hWaveOuts ) + + +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; + PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; + PaWinMmeStream *stream = 0; + PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; + int inputChannelCount, outputChannelCount; + PaSampleFormat inputSampleFormat, outputSampleFormat; + double suggestedInputLatency, suggestedOutputLatency; + PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; + unsigned long bytesPerInputFrame, bytesPerOutputFrame; + unsigned long framesPerHostInputBuffer; + unsigned long numHostInputBuffers; + unsigned long framesPerHostOutputBuffer; + unsigned long numHostOutputBuffers; + unsigned long framesPerBufferProcessorCall; + int lockInited = 0; + int bufferEventInited = 0; + int abortEventInited = 0; + WAVEFORMATEX wfx; + MMRESULT mmresult; + unsigned int i; + int channelCount; + PaWinMmeDeviceAndChannelCount *inputDevices = 0; + unsigned long numInputDevices = (inputParameters) ? 1 : 0; + PaWinMmeDeviceAndChannelCount *outputDevices = 0; + unsigned long numOutputDevices = (outputParameters) ? 1 : 0; + char noHighPriorityProcessClass = 0; + char useTimeCriticalProcessingThreadPriority = 0; + char throttleProcessingThreadOnOverload = 1; + + + if( inputParameters ) + { + inputChannelCount = inputParameters->channelCount; + inputSampleFormat = inputParameters->sampleFormat; + suggestedInputLatency = inputParameters->suggestedLatency; + + /* check that input device can support inputChannelCount */ + if( (inputParameters->device != paUseHostApiSpecificDeviceSpecification) && + (inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels) ) + return paInvalidChannelCount; + + + /* validate input hostApiSpecificStreamInfo */ + inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo; + if( inputStreamInfo ) + { + if( inputStreamInfo->size != sizeof( PaWinMmeStreamInfo ) + || inputStreamInfo->version != 1 ) + { + return paIncompatibleHostApiSpecificStreamInfo; + } + + if( inputStreamInfo->flags & PaWinMmeNoHighPriorityProcessClass ) + noHighPriorityProcessClass = 1; + if( inputStreamInfo->flags & PaWinMmeDontThrottleOverloadedProcessingThread ) + throttleProcessingThreadOnOverload = 0; + if( inputStreamInfo->flags & PaWinMmeUseTimeCriticalThreadPriority ) + useTimeCriticalProcessingThreadPriority = 1; + + /* validate multidevice fields */ + + if( inputStreamInfo->flags & PaWinMmeUseMultipleDevices ) + { + int totalChannels = 0; + for( i=0; i< inputStreamInfo->deviceCount; ++i ) + { + /* validate that the device number is within range, and that + the number of channels is legal */ + PaDeviceIndex hostApiDevice; + + if( inputParameters->device != paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + channelCount = inputStreamInfo->devices[i].channelCount; + + result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, + inputStreamInfo->devices[i].device, hostApi ); + if( result != paNoError ) + return result; + + if( channelCount < 1 || channelCount > hostApi->deviceInfos[ hostApiDevice ]->maxInputChannels ) + return paInvalidChannelCount; + + /* FIXME this validation might be easier and better if there was a pautil + function which performed the validation in pa_front:ValidateOpenStreamParameters() */ + + totalChannels += channelCount; + } + + if( totalChannels != inputChannelCount ) + { + /* inputChannelCount must match total channels specified by multiple devices */ + return paInvalidChannelCount; /* REVIEW use of this error code */ + } + + inputDevices = inputStreamInfo->devices; + numInputDevices = inputStreamInfo->deviceCount; + } + } + + /* FIXME: establish which host formats are available */ + hostInputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); + + } + else + { + inputChannelCount = 0; + inputSampleFormat = -1; + suggestedInputLatency = 0.; + inputStreamInfo = 0; + hostInputSampleFormat = -1; + } + + + if( outputParameters ) + { + outputChannelCount = outputParameters->channelCount; + outputSampleFormat = outputParameters->sampleFormat; + suggestedOutputLatency = outputParameters->suggestedLatency; + + /* check that input device can support inputChannelCount */ + if( (outputParameters->device != paUseHostApiSpecificDeviceSpecification) && + (inputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels) ) + return paInvalidChannelCount; + + + /* validate input hostApiSpecificStreamInfo */ + outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo; + if( outputStreamInfo ) + { + if( outputStreamInfo->size != sizeof( PaWinMmeStreamInfo ) + || outputStreamInfo->version != 1 ) + { + return paIncompatibleHostApiSpecificStreamInfo; + } + + if( outputStreamInfo->flags & PaWinMmeNoHighPriorityProcessClass ) + noHighPriorityProcessClass = 1; + if( outputStreamInfo->flags & PaWinMmeDontThrottleOverloadedProcessingThread ) + throttleProcessingThreadOnOverload = 0; + if( outputStreamInfo->flags & PaWinMmeUseTimeCriticalThreadPriority ) + useTimeCriticalProcessingThreadPriority = 1; + + /* validate multidevice fields */ + + if( outputStreamInfo->flags & PaWinMmeUseMultipleDevices ) + { + int totalChannels = 0; + for( i=0; i< outputStreamInfo->deviceCount; ++i ) + { + /* validate that the device number is within range, and that + the number of channels is legal */ + PaDeviceIndex hostApiDevice; + + if( outputParameters->device != paUseHostApiSpecificDeviceSpecification ) + return paInvalidDevice; + + channelCount = outputStreamInfo->devices[i].channelCount; + + result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, + outputStreamInfo->devices[i].device, + hostApi ); + if( result != paNoError ) + return result; + + if( channelCount < 1 || channelCount > hostApi->deviceInfos[ hostApiDevice ]->maxOutputChannels ) + return paInvalidChannelCount; + + /* FIXME this validation might be easier and better if there was a pautil + function which performed the validation in pa_front:ValidateOpenStreamParameters() */ + + totalChannels += channelCount; + } + + if( totalChannels != outputChannelCount ) + { + /* outputChannelCount must match total channels specified by multiple devices */ + return paInvalidChannelCount; /* REVIEW use of this error code */ + } + + outputDevices = outputStreamInfo->devices; + numOutputDevices = outputStreamInfo->deviceCount; + } + } + + /* FIXME: establish which host formats are available */ + hostOutputSampleFormat = + PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); + } + else + { + outputChannelCount = 0; + outputSampleFormat = -1; + outputStreamInfo = 0; + hostOutputSampleFormat = -1; + suggestedOutputLatency = 0.; + } + + + /* + IMPLEMENT ME: + - alter sampleRate to a close allowable rate if possible / necessary + */ + + + /* validate platform specific flags */ + if( (streamFlags & paPlatformSpecificFlags) != 0 ) + return paInvalidFlag; /* unexpected platform specific flag */ + + + result = CalculateBufferSettings( &framesPerHostInputBuffer, &numHostInputBuffers, + &framesPerHostOutputBuffer, &numHostOutputBuffers, + inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo, + outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo, + sampleRate, framesPerBuffer ); + if( result != paNoError ) + goto error; + + + stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) ); + if( !stream ) + { + result = paInsufficientMemory; + goto error; + } + + stream->hWaveIns = 0; + stream->inputBuffers = 0; + stream->hWaveOuts = 0; + stream->outputBuffers = 0; + stream->processingThread = 0; + + stream->noHighPriorityProcessClass = noHighPriorityProcessClass; + stream->useTimeCriticalProcessingThreadPriority = useTimeCriticalProcessingThreadPriority; + stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload; + + PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, + &winMmeHostApi->callbackStreamInterface, streamCallback, userData ); + + stream->streamRepresentation.streamInfo.inputLatency = (double)(framesPerHostInputBuffer * (numHostInputBuffers-1)) / sampleRate; + stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerHostOutputBuffer * (numHostOutputBuffers-1)) / sampleRate; + stream->streamRepresentation.streamInfo.sampleRate = sampleRate; + + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); + + + if( inputParameters && outputParameters ) /* full duplex */ + { + /* + either host input and output buffers must be the same size, or the + larger one must be an integer multiple of the smaller one. + FIXME: should this return an error if the host specific latency + settings don't fulfill these constraints? rb: probably + */ + + if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) + { + assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); + + framesPerBufferProcessorCall = framesPerHostInputBuffer; + } + else + { + assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); + + framesPerBufferProcessorCall = framesPerHostOutputBuffer; + } + } + else if( inputParameters ) + { + framesPerBufferProcessorCall = framesPerHostInputBuffer; + } + else if( outputParameters ) + { + framesPerBufferProcessorCall = framesPerHostOutputBuffer; + } + + stream->framesPerInputBuffer = framesPerHostInputBuffer; + stream->framesPerOutputBuffer = framesPerHostOutputBuffer; + + result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, + inputChannelCount, inputSampleFormat, hostInputSampleFormat, + outputChannelCount, outputSampleFormat, hostOutputSampleFormat, + sampleRate, streamFlags, framesPerBuffer, + framesPerBufferProcessorCall, paUtilFixedHostBufferSize, + streamCallback, userData ); + if( result != paNoError ) + goto error; + + stream->isActive = 0; + + stream->streamPosition = 0.; + stream->previousStreamPosition = 0; + + + stream->bufferEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); + if( stream->bufferEvent == NULL ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + bufferEventInited = 1; + + if( inputParameters ) + { + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nSamplesPerSec = (DWORD) sampleRate; + wfx.cbSize = 0; + + stream->numInputDevices = numInputDevices; + stream->hWaveIns = (HWAVEIN*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * stream->numInputDevices ); + if( !stream->hWaveIns ) + { + result = paInsufficientMemory; + goto error; + } + + for( i = 0; i < stream->numInputDevices; ++i ) + stream->hWaveIns[i] = 0; + + for( i = 0; i < stream->numInputDevices; ++i ) + { + int inputWinMmeId; + + if( inputDevices ) + { + PaDeviceIndex hostApiDevice; + + result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, + inputDevices[i].device, hostApi ); + if( result != paNoError ) + return result; + + inputWinMmeId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, hostApiDevice ); + wfx.nChannels = (WORD) inputDevices[i].channelCount; + } + else + { + inputWinMmeId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device ); + wfx.nChannels = (WORD) inputChannelCount; + } + + bytesPerInputFrame = wfx.nChannels * stream->bufferProcessor.bytesPerHostInputSample; + + wfx.nAvgBytesPerSec = (DWORD)(bytesPerInputFrame * sampleRate); + wfx.nBlockAlign = (WORD)bytesPerInputFrame; + wfx.wBitsPerSample = (WORD)((bytesPerInputFrame/wfx.nChannels) * 8); + + /* REVIEW: consider not firing an event for input when a full duplex stream is being used */ + + mmresult = waveInOpen( &stream->hWaveIns[i], inputWinMmeId, &wfx, + (DWORD)stream->bufferEvent, (DWORD) stream, CALLBACK_EVENT ); + if( mmresult != MMSYSERR_NOERROR ) + { + switch( mmresult ) + { + case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ + result = paDeviceUnavailable; + break; + case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ + result = paInternalError; /* portaudio should ensure that only good device ids are used */ + break; + case MMSYSERR_NODRIVER: /* No device driver is present. */ + result = paDeviceUnavailable; + break; + case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ + result = paInsufficientMemory; + break; + case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ + result = paInternalError; /* REVIEW: port audio shouldn't get this far without using compatible format info */ + break; + default: + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + } + goto error; + } + } + } + + if( outputParameters ) + { + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nSamplesPerSec = (DWORD) sampleRate; + wfx.cbSize = 0; + + stream->numOutputDevices = numOutputDevices; + stream->hWaveOuts = (HWAVEOUT*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * stream->numOutputDevices ); + if( !stream->hWaveOuts ) + { + result = paInsufficientMemory; + goto error; + } + + for( i = 0; i < stream->numOutputDevices; ++i ) + stream->hWaveOuts[i] = 0; + + for( i = 0; i < stream->numOutputDevices; ++i ) + { + int outputWinMmeId; + + if( outputDevices ) + { + PaDeviceIndex hostApiDevice; + + result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, + outputDevices[i].device, hostApi ); + if( result != paNoError ) + return result; + + outputWinMmeId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, hostApiDevice ); + wfx.nChannels = (WORD) outputDevices[i].channelCount; + } + else + { + outputWinMmeId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device ); + wfx.nChannels = (WORD) outputChannelCount; + } + + bytesPerOutputFrame = wfx.nChannels * stream->bufferProcessor.bytesPerHostOutputSample; + + wfx.nAvgBytesPerSec = (DWORD)(bytesPerOutputFrame * sampleRate); + wfx.nBlockAlign = (WORD)bytesPerOutputFrame; + wfx.wBitsPerSample = (WORD)((bytesPerOutputFrame/wfx.nChannels) * 8); + + mmresult = waveOutOpen( &stream->hWaveOuts[i], outputWinMmeId, &wfx, + (DWORD)stream->bufferEvent, (DWORD) stream, CALLBACK_EVENT ); + if( mmresult != MMSYSERR_NOERROR ) + { + switch( mmresult ) + { + case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ + result = paDeviceUnavailable; + break; + case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ + result = paInternalError; /* portaudio should ensure that only good device ids are used */ + break; + case MMSYSERR_NODRIVER: /* No device driver is present. */ + result = paDeviceUnavailable; + break; + case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ + result = paInsufficientMemory; + break; + case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ + result = paInternalError; /* REVIEW: port audio shouldn't get this far without using compatible format info */ + break; + default: + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + } + goto error; + } + } + } + + if( PA_IS_INPUT_STREAM_(stream) ) + { + stream->inputBuffers = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * stream->numInputDevices ); + if( stream->inputBuffers == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + for( i =0; i < stream->numInputDevices; ++i ) + stream->inputBuffers[i] = 0; + + stream->numInputBuffers = numHostInputBuffers; + + for( i =0; i < stream->numInputDevices; ++i ) + { + int hostInputBufferBytes = Pa_GetSampleSize( hostInputSampleFormat ) * + framesPerHostInputBuffer * + ((inputDevices) ? inputDevices[i].channelCount : inputChannelCount); + if( hostInputBufferBytes < 0 ) + { + result = paInternalError; + goto error; + } + + result = InitializeBufferSet( &stream->inputBuffers[i], numHostInputBuffers, hostInputBufferBytes, + 1 /* isInput */, + (MmeHandle)stream->hWaveIns[i], + ((inputDevices) ? inputDevices[i].channelCount : inputChannelCount) ); + + if( result != paNoError ) + goto error; + } + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + stream->outputBuffers = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * stream->numOutputDevices ); + if( stream->outputBuffers == 0 ) + { + result = paInsufficientMemory; + goto error; + } + + for( i =0; i < stream->numOutputDevices; ++i ) + stream->outputBuffers[i] = 0; + + stream->numOutputBuffers = numHostOutputBuffers; + + for( i=0; i < stream->numOutputDevices; ++i ) + { + int hostOutputBufferBytes = Pa_GetSampleSize( hostOutputSampleFormat ) * + framesPerHostOutputBuffer * + ((outputDevices) ? outputDevices[i].channelCount : outputChannelCount); + if( hostOutputBufferBytes < 0 ) + { + result = paInternalError; + goto error; + } + + result = InitializeBufferSet( &stream->outputBuffers[i], numHostOutputBuffers, hostOutputBufferBytes, + 0 /* not isInput */, + (MmeHandle)stream->hWaveOuts[i], + ((outputDevices) ? outputDevices[i].channelCount : outputChannelCount) ); + + if( result != paNoError ) + goto error; + } + } + + stream->abortEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + if( stream->abortEvent == NULL ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + abortEventInited = 1; + + InitializeCriticalSection( &stream->lock ); + lockInited = 1; + + if( PA_IS_OUTPUT_STREAM_(stream) ) + stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->numOutputBuffers) / sampleRate); + else + stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->numInputBuffers) / sampleRate); + + + *s = (PaStream*)stream; + + return result; + +error: + if( lockInited ) + DeleteCriticalSection( &stream->lock ); + + if( abortEventInited ) + CloseHandle( stream->abortEvent ); + + + if( stream->inputBuffers ) + { + for( i =0 ; i< stream->numInputDevices; ++i ) + { + if( stream->inputBuffers[i] ) + { + TerminateBufferSet( &stream->inputBuffers[i], stream->numInputBuffers, + 1 /* isInput */, (MmeHandle)stream->hWaveIns[i] ); + } + } + + PaUtil_FreeMemory( stream->inputBuffers ); + } + + if( stream->outputBuffers ) + { + for( i =0 ; i< stream->numOutputDevices; ++i ) + { + if( stream->outputBuffers[i] ) + { + TerminateBufferSet( &stream->outputBuffers[i], stream->numOutputBuffers, + 0 /* not isInput */, (MmeHandle)stream->hWaveOuts[i] ); + } + } + + PaUtil_FreeMemory( stream->outputBuffers ); + } + + if( stream->hWaveIns ) + { + for( i =0 ; i< stream->numInputDevices; ++i ) + { + if( stream->hWaveIns[i] ) + waveInClose( stream->hWaveIns[i] ); + } + + PaUtil_FreeMemory( stream->hWaveIns ); + } + + if( stream->hWaveOuts ) + { + for( i =0 ; i< stream->numOutputDevices; ++i ) + { + if( stream->hWaveOuts[i] ) + waveOutClose( stream->hWaveOuts[i] ); + } + + PaUtil_FreeMemory( stream->hWaveOuts ); + } + + if( bufferEventInited ) + CloseHandle( stream->bufferEvent ); + + if( stream ) + PaUtil_FreeMemory( stream ); + + return result; +} + + +/* return non-zero if any output buffers are queued */ +static int OutputBuffersAreQueued( PaWinMmeStream *stream ) +{ + int result = 0; + unsigned int i, j; + + if( PA_IS_OUTPUT_STREAM_( stream ) ) + { + for( i=0; inumOutputBuffers; ++i ) + { + for( j=0; j < stream->numOutputDevices; ++j ) + { + if( !( stream->outputBuffers[ j ][ i ].dwFlags & WHDR_DONE) ) + { + result++; + } + } + } + } + + return result; +} + + +static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + unsigned int i; + + for( i=0; i< stream->numInputDevices; ++i ) + { + mmresult = waveInAddBuffer( stream->hWaveIns[i], + &stream->inputBuffers[i][ stream->currentInputBufferIndex ], + sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + } + } + stream->currentInputBufferIndex = (stream->currentInputBufferIndex+1 >= stream->numInputBuffers) ? + 0 : stream->currentInputBufferIndex+1; + + stream->framesUsedInCurrentInputBuffer = 0; + + return result; +} + + +static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream ) +{ + PaError result = paNoError; + MMRESULT mmresult; + unsigned int i; + + for( i=0; i< stream->numOutputDevices; ++i ) + { + mmresult = waveOutWrite( stream->hWaveOuts[i], + &stream->outputBuffers[i][ stream->currentOutputBufferIndex ], + sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + } + } + + stream->currentOutputBufferIndex = (stream->currentOutputBufferIndex+1 >= stream->numOutputBuffers) ? + 0 : stream->currentOutputBufferIndex+1; + + stream->framesUsedInCurrentOutputBuffer = 0; + + return result; +} + + +static DWORD WINAPI ProcessingThreadProc( void *pArg ) +{ + PaWinMmeStream *stream = (PaWinMmeStream *)pArg; + HANDLE events[2]; + int numEvents = 0; + DWORD result = paNoError; + DWORD waitResult; +/** @todo: +Gordon Gidluck: +> function: ProcessingThreadProc() +> line #1665 DWORD timeout = stream->allBuffersDurationMs * 0.5; +> conversion from 'double ' to 'unsigned long ', possible loss of data +*/ + DWORD timeout = stream->allBuffersDurationMs * 0.5; + DWORD numTimeouts = 0; + int hostBuffersAvailable; + signed int hostInputBufferIndex, hostOutputBufferIndex; + int callbackResult; + int done = 0; + unsigned int channel, i, j; + unsigned long framesProcessed; + + /* prepare event array for call to WaitForMultipleObjects() */ + events[numEvents++] = stream->bufferEvent; + events[numEvents++] = stream->abortEvent; + + /* loop until something causes us to stop */ + while( !done ) + { + /* wait for MME to signal that a buffer is available, or for + the PA abort event to be signaled */ + waitResult = WaitForMultipleObjects( numEvents, events, FALSE, timeout ); + if( waitResult == WAIT_FAILED ) + { + result = paUnanticipatedHostError; + /* FIXME/REVIEW: can't return host error info from an asyncronous thread */ + done = 1; + } + else if( waitResult == WAIT_TIMEOUT ) + { + /* if a timeout is encountered, continue */ + numTimeouts += 1; + } + + if( stream->abortProcessing ) + { + /* Pa_AbortStream() has been called, stop processing immediately */ + done = 1; + } + else if( stream->stopProcessing ) + { + /* Pa_StopStream() has been called or the user callback returned + non-zero, processing will continue until all output buffers + are marked as done. The stream will stop immediately if it + is input-only. + */ + + if( !OutputBuffersAreQueued( stream ) ) + { + done = 1; /* Will cause thread to return. */ + } + } + else + { + hostBuffersAvailable = 1; + + /* process all available host buffers */ + do + { + hostInputBufferIndex = -1; + hostOutputBufferIndex = -1; + + if( PA_IS_INPUT_STREAM_(stream)) + { + hostInputBufferIndex = stream->currentInputBufferIndex; + for( i=0; inumInputDevices; ++i ) + { + if( !(stream->inputBuffers[i][ stream->currentInputBufferIndex ].dwFlags & WHDR_DONE) ) + { + hostInputBufferIndex = -1; + break; + } + } + + if( hostInputBufferIndex != -1 ) + { + /* if all of the other buffers are also ready then we dicard all but the + most recent. */ + int inputCatchUp = 1; + + for( i=0; i < stream->numInputBuffers && inputCatchUp == 1; ++i ) + { + for( j=0; jnumInputDevices; ++j ) + { + if( !(stream->inputBuffers[ j ][ i ].dwFlags & WHDR_DONE) ) + { + inputCatchUp = 0; + break; + } + } + } + + if( inputCatchUp ) + { + for( i=0; i < stream->numInputBuffers - 1; ++i ) + { + result = AdvanceToNextInputBuffer( stream ); + if( result != paNoError ) + done = 1; + } + } + } + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + hostOutputBufferIndex = stream->currentOutputBufferIndex; + for( i=0; inumOutputDevices; ++i ) + { + if( !(stream->outputBuffers[i][ stream->currentOutputBufferIndex ].dwFlags & WHDR_DONE) ) + { + hostOutputBufferIndex = -1; + break; + } + } + + if( hostOutputBufferIndex != - 1 ) + { + /* if all of the other buffers are also ready, catch up by copying + the most recently generated buffer into all but one of the output + buffers */ + int outputCatchUp = 1; + + for( i=0; i < stream->numOutputBuffers && outputCatchUp == 1; ++i ) + { + for( j=0; jnumOutputDevices; ++j ) + { + if( !(stream->outputBuffers[ j ][ i ].dwFlags & WHDR_DONE) ) + { + outputCatchUp = 0; + break; + } + } + } + + if( outputCatchUp ) + { + /* FIXME: this is an output underflow buffer slip and should be flagged as such */ + unsigned int previousBufferIndex = (stream->currentOutputBufferIndex==0) + ? stream->numOutputBuffers - 1 + : stream->currentOutputBufferIndex - 1; + + for( i=0; i < stream->numOutputBuffers - 1; ++i ) + { + for( j=0; jnumOutputDevices; ++j ) + { + if( stream->outputBuffers[j][ stream->currentOutputBufferIndex ].lpData + != stream->outputBuffers[j][ previousBufferIndex ].lpData ) + { + CopyMemory( stream->outputBuffers[j][ stream->currentOutputBufferIndex ].lpData, + stream->outputBuffers[j][ previousBufferIndex ].lpData, + stream->outputBuffers[j][ stream->currentOutputBufferIndex ].dwBufferLength ); + } + } + + result = AdvanceToNextOutputBuffer( stream ); + if( result != paNoError ) + done = 1; + } + } + } + } + + + if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) || + (!PA_IS_FULL_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) ) + { + PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* @todo implement inputBufferAdcTime and currentTime */ + + + if( hostOutputBufferIndex != -1 ){ + MMTIME time; + double now; + long totalRingFrames; + long ringPosition; + long playbackPosition; + + time.wType = TIME_SAMPLES; + waveOutGetPosition( stream->hWaveOuts[0], &time, sizeof(MMTIME) ); + now = PaUtil_GetTime(); + + totalRingFrames = stream->numOutputBuffers * stream->bufferProcessor.framesPerHostBuffer; + + ringPosition = stream->currentOutputBufferIndex * stream->bufferProcessor.framesPerHostBuffer; + + playbackPosition = time.u.sample % totalRingFrames; + + if( playbackPosition >= ringPosition ){ + timeInfo.outputBufferDacTime = + now + ((double)( ringPosition + (totalRingFrames - playbackPosition) ) * stream->bufferProcessor.samplePeriod ); + }else{ + timeInfo.outputBufferDacTime = + now + ((double)( ringPosition - playbackPosition ) * stream->bufferProcessor.samplePeriod ); + } + } + + + PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); + + PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo ); + + if( hostInputBufferIndex != -1 ) + { + PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + + channel = 0; + for( i=0; inumInputDevices; ++i ) + { + /* we have stored the number of channels in the buffer in dwUser */ + int channelCount = stream->inputBuffers[i][ hostInputBufferIndex ].dwUser; + + PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, + stream->inputBuffers[i][ hostInputBufferIndex ].lpData + + stream->framesUsedInCurrentInputBuffer * channelCount * + stream->bufferProcessor.bytesPerHostInputSample, + channelCount ); + + + channel += channelCount; + } + } + + if( hostOutputBufferIndex != -1 ) + { + PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); + + channel = 0; + for( i=0; inumOutputDevices; ++i ) + { + /* we have stored the number of channels in the buffer in dwUser */ + int channelCount = stream->outputBuffers[i][ hostOutputBufferIndex ].dwUser; + + PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, + stream->outputBuffers[i][ hostOutputBufferIndex ].lpData + + stream->framesUsedInCurrentOutputBuffer * channelCount * + stream->bufferProcessor.bytesPerHostOutputSample, + channelCount ); + + /* we have stored the number of channels in the buffer in dwUser */ + channel += channelCount; + } + } + + framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); + + stream->framesUsedInCurrentInputBuffer += framesProcessed; + stream->framesUsedInCurrentOutputBuffer += framesProcessed; + + PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); + + if( callbackResult == paContinue ) + { + /* nothing special to do */ + } + else if( callbackResult == paAbort ) + { + stream->abortProcessing = 1; + done = 1; + /* FIXME: should probably do a reset here */ + result = paNoError; + } + else + { + /* User cllback has asked us to stop with paComplete or other non-zero value */ + stream->stopProcessing = 1; /* stop once currently queued audio has finished */ + result = paNoError; + } + + /* + FIXME: the following code is incorrect, because stopProcessing should + still queue the current buffer. + */ + if( stream->stopProcessing == 0 && stream->abortProcessing == 0 ) + { + if( stream->throttleProcessingThreadOnOverload != 0 ) + { + if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. ) + { + if( stream->processingThreadPriority != stream->throttledThreadPriority ) + { + SetThreadPriority( stream->processingThread, stream->throttledThreadPriority ); + stream->processingThreadPriority = stream->throttledThreadPriority; + } + +/** @todo: +Gordon Gidluck: +> function: ProcessingThreadProc() +> line #1947/1948 Sleep( stream->bufferProcessor.framesPerHostBuffer * +> stream->bufferProcessor.samplePeriod * .25 ); +> conversion from 'double ' to 'unsigned long ', possible loss of data +> integral size mismatch in argument; conversion supplied +*/ + /* sleep for a quater of a buffer's duration to give other processes a go */ + Sleep( stream->bufferProcessor.framesPerHostBuffer * + stream->bufferProcessor.samplePeriod * .25 ); + } + else + { + if( stream->processingThreadPriority != stream->highThreadPriority ) + { + SetThreadPriority( stream->processingThread, stream->highThreadPriority ); + stream->processingThreadPriority = stream->highThreadPriority; + } + } + } + + if( PA_IS_INPUT_STREAM_(stream) && + stream->framesUsedInCurrentInputBuffer == stream->framesPerInputBuffer ) + { + result = AdvanceToNextInputBuffer( stream ); + if( result != paNoError ) + done = 1; + } + + if( PA_IS_OUTPUT_STREAM_(stream) && + stream->framesUsedInCurrentOutputBuffer == stream->framesPerOutputBuffer ) + { + result = AdvanceToNextOutputBuffer( stream ); + if( result != paNoError ) + done = 1; + } + } + } + else + { + hostBuffersAvailable = 0; + } + } + while( hostBuffersAvailable && + stream->stopProcessing == 0 && + stream->abortProcessing == 0 && + !done ); + } + + result = UpdateStreamTime( stream ); + if( result != paNoError ) + done = 1; + } + + stream->isActive = 0; + + if( stream->streamRepresentation.streamFinishedCallback != 0 ) + stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); + + 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; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + MMRESULT mmresult; + unsigned int i; + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; inumInputDevices; ++i ) + { + TerminateBufferSet( &stream->inputBuffers[i], stream->numInputBuffers, + 1 /* isInput */, (MmeHandle)stream->hWaveIns[i] ); + } + + PaUtil_FreeMemory( stream->inputBuffers ); + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i=0; inumOutputDevices; ++i ) + { + TerminateBufferSet( &stream->outputBuffers[i], stream->numOutputBuffers, + 0 /* not isInput */, (MmeHandle)stream->hWaveOuts[i] ); + } + + PaUtil_FreeMemory( stream->outputBuffers ); + } + + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; inumInputDevices; ++i ) + { + mmresult = waveInClose( stream->hWaveIns[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + goto error; + } + } + + PaUtil_FreeMemory( stream->hWaveIns ); + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i=0; inumOutputDevices; ++i ) + { + mmresult = waveOutClose( stream->hWaveOuts[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + goto error; + } + } + + PaUtil_FreeMemory( stream->hWaveOuts ); + } + + if( CloseHandle( stream->bufferEvent ) == 0 ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + + if( CloseHandle( stream->abortEvent ) == 0 ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + + DeleteCriticalSection( &stream->lock ); + + PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); + PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); + PaUtil_FreeMemory( stream ); + +error: + /* FIXME: consider how to best clean up on failure */ + return result; +} + + +static PaError StartStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + MMRESULT mmresult; + unsigned int i, j; + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; inumInputBuffers; ++i ) + { + for( j=0; jnumInputDevices; ++j ) + { + mmresult = waveInAddBuffer( stream->hWaveIns[j], &stream->inputBuffers[j][i], sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + goto error; + } + } + } + stream->currentInputBufferIndex = 0; + stream->framesUsedInCurrentInputBuffer = 0; + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i=0; inumOutputDevices; ++i ) + { + if( (mmresult = waveOutPause( stream->hWaveOuts[i] )) != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + goto error; + } + } + + for( i=0; inumOutputBuffers; ++i ) + { + for( j=0; jnumOutputDevices; ++j ) + { + ZeroMemory( stream->outputBuffers[j][i].lpData, stream->outputBuffers[j][i].dwBufferLength ); + mmresult = waveOutWrite( stream->hWaveOuts[j], &stream->outputBuffers[j][i], sizeof(WAVEHDR) ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + goto error; + } + } + } + stream->currentOutputBufferIndex = 0; + stream->framesUsedInCurrentOutputBuffer = 0; + } + + stream->streamPosition = 0.; + stream->previousStreamPosition = 0; + + stream->isActive = 1; + stream->stopProcessing = 0; + stream->abortProcessing = 0; + + if( ResetEvent( stream->bufferEvent ) == 0 ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + + if( ResetEvent( stream->abortEvent ) == 0 ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + + /* Create thread that waits for audio buffers to be ready for processing. */ + stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ); + if( !stream->processingThread ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + + /* I used to pass the thread which was failing. I now pass GetCurrentProcess(). + * This fix could improve latency for some applications. It could also result in CPU + * starvation if the callback did too much processing. + * I also added result checks, so we might see more failures at initialization. + * Thanks to Alberto di Bene for spotting this. + */ + /* REVIEW: should we reset the priority class when the stream has stopped? + - would be best to refcount priority boosts incase more than one + stream is open + */ + + if( !stream->noHighPriorityProcessClass ) + { +#ifndef WIN32_PLATFORM_PSPC /* no SetPriorityClass or HIGH_PRIORITY_CLASS on PocketPC */ + + if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) /* PLB20010816 */ + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } +#endif + } + + if( stream->useTimeCriticalProcessingThreadPriority ) + stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL; + else + stream->highThreadPriority = THREAD_PRIORITY_HIGHEST; + + stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL; + + if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); + goto error; + } + stream->processingThreadPriority = stream->highThreadPriority; + + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; i < stream->numInputDevices; ++i ) + { + mmresult = waveInStart( stream->hWaveIns[i] ); + PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult)); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + goto error; + } + } + } + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i=0; i < stream->numOutputDevices; ++i ) + { + if( (mmresult = waveOutRestart( stream->hWaveOuts[i] )) != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + goto error; + } + } + } + + return result; + +error: + /* FIXME: implement recovery as best we can + This should involve rolling back to a state as-if this function had never been called + */ + return result; +} + + +static PaError StopStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + int timeout; + DWORD waitResult; + MMRESULT mmresult; + unsigned int i; + + /* + FIXME: the error checking in this function needs review. the basic + idea is to return from this function in a known state - for example + there is no point avoiding calling waveInReset just because + the thread times out. + */ + + + /* Tell processing thread to stop generating more data and to let current data play out. */ + stream->stopProcessing = 1; + + /* Calculate timeOut longer than longest time it could take to return all buffers. */ + timeout = stream->allBuffersDurationMs * 1.5; + if( timeout < PA_MIN_TIMEOUT_MSEC_ ) + timeout = PA_MIN_TIMEOUT_MSEC_; + + PA_DEBUG(("WinMME StopStream: waiting for background thread.\n")); + + waitResult = WaitForSingleObject( stream->processingThread, timeout ); + if( waitResult == WAIT_TIMEOUT ) + { + /* try to abort */ + stream->abortProcessing = 1; + SetEvent( stream->abortEvent ); + waitResult = WaitForSingleObject( stream->processingThread, timeout ); + if( waitResult == WAIT_TIMEOUT ) + { + PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n")); + result = paTimedOut; + } + } + + CloseHandle( stream->processingThread ); + stream->processingThread = NULL; + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i =0; i < stream->numOutputDevices; ++i ) + { + mmresult = waveOutReset( stream->hWaveOuts[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + } + } + } + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; i < stream->numInputDevices; ++i ) + { + mmresult = waveInReset( stream->hWaveIns[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + result = paUnanticipatedHostError; + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + } + } + } + + stream->isActive = 0; + + return result; +} + + +static PaError AbortStream( PaStream *s ) +{ + PaError result = paNoError; + PaWinMmeStream *stream = (PaWinMmeStream*)s; + int timeout; + DWORD waitResult; + MMRESULT mmresult; + unsigned int i; + + /* + FIXME: the error checking in this function needs review. the basic + idea is to return from this function in a known state - for example + there is no point avoiding calling waveInReset just because + the thread times out. + */ + + /* Tell processing thread to abort immediately */ + stream->abortProcessing = 1; + SetEvent( stream->abortEvent ); + + /* Calculate timeOut longer than longest time it could take to return all buffers. */ + timeout = stream->allBuffersDurationMs * 1.5; + if( timeout < PA_MIN_TIMEOUT_MSEC_ ) + timeout = PA_MIN_TIMEOUT_MSEC_; + + if( PA_IS_OUTPUT_STREAM_(stream) ) + { + for( i =0; i < stream->numOutputDevices; ++i ) + { + mmresult = waveOutReset( stream->hWaveOuts[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + return paUnanticipatedHostError; + } + } + } + + if( PA_IS_INPUT_STREAM_(stream) ) + { + for( i=0; i < stream->numInputDevices; ++i ) + { + mmresult = waveInReset( stream->hWaveIns[i] ); + if( mmresult != MMSYSERR_NOERROR ) + { + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + return paUnanticipatedHostError; + } + } + } + + + PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n")); + + waitResult = WaitForSingleObject( stream->processingThread, timeout ); + if( waitResult == WAIT_TIMEOUT ) + { + PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n")); + return paTimedOut; + } + + CloseHandle( stream->processingThread ); + stream->processingThread = NULL; + + stream->isActive = 0; + + return result; +} + + +static PaError IsStreamStopped( PaStream *s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + return ( stream->processingThread == NULL ); +} + + +static PaError IsStreamActive( PaStream *s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + return stream->isActive; +} + + +/* UpdateStreamTime() must be called periodically because mmtime.u.sample + is a DWORD and can wrap and lose sync after a few hours. + */ +static PaError UpdateStreamTime( PaWinMmeStream *stream ) +{ + MMRESULT mmresult; + MMTIME mmtime; + mmtime.wType = TIME_SAMPLES; + + if( stream->hWaveOuts ) + { + /* assume that all devices have the same position */ + mmresult = waveOutGetPosition( stream->hWaveOuts[0], &mmtime, sizeof(mmtime) ); + + if( mmresult != MMSYSERR_NOERROR ) + { + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + return paUnanticipatedHostError; + } + } + else + { + /* assume that all devices have the same position */ + mmresult = waveInGetPosition( stream->hWaveIns[0], &mmtime, sizeof(mmtime) ); + + if( mmresult != MMSYSERR_NOERROR ) + { + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + return paUnanticipatedHostError; + } + } + + + /* This data has two variables and is shared by foreground and background. + * So we need to make it thread safe. */ + EnterCriticalSection( &stream->lock ); + stream->streamPosition += ((long)mmtime.u.sample) - stream->previousStreamPosition; + stream->previousStreamPosition = (long)mmtime.u.sample; + LeaveCriticalSection( &stream->lock ); + + return paNoError; +} + + +static PaTime GetStreamTime( PaStream *s ) +{ +/* + new behavior for GetStreamTime is to return a stream based seconds clock + used for the outTime parameter to the callback. + FIXME: delete this comment when the other unnecessary related code has + been cleaned from this file. + + PaWinMmeStream *stream = (PaWinMmeStream*)s; + PaError error = UpdateStreamTime( stream ); + + if( error == paNoError ) + return stream->streamPosition; + else + return 0; +*/ + (void) s; /* unused parameter */ + return PaUtil_GetTime(); +} + + +static double GetStreamCpuLoad( PaStream* s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)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 ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + (void) stream; /* unused parameters */ + (void) buffer; + (void) frames; + + return paNoError; +} + + +static PaError WriteStream( PaStream* s, + void *buffer, + unsigned long frames ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)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 ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + (void) stream; /* unused parameter */ + + return 0; +} + + +static signed long GetStreamWriteAvailable( PaStream* s ) +{ + PaWinMmeStream *stream = (PaWinMmeStream*)s; + + /* IMPLEMENT ME, see portaudio.h for required behavior*/ + (void) stream; /* unused parameter */ + + return 0; +} + + + + diff --git a/pd/portaudio/pa_win_wmme/pa_win_wmme.h b/pd/portaudio/pa_win_wmme/pa_win_wmme.h new file mode 100644 index 00000000..d5c18a28 --- /dev/null +++ b/pd/portaudio/pa_win_wmme/pa_win_wmme.h @@ -0,0 +1,105 @@ +#ifndef PA_WIN_WMME_H +#define PA_WIN_WMME_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * + * PortAudio Portable Real-Time Audio Library + * MME 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. + * + */ + + +#include "portaudio.h" + +#define PaWinMmeUseLowLevelLatencyParameters (0x01) +#define PaWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */ + +/* by default, the mme implementation boosts the process priority class to + HIGH_PRIORITY_CLASS. This flag disables that priority boost */ +#define PaWinMmeNoHighPriorityProcessClass (0x03) + +/* by default, the mme implementation drops the processing thread's priority + to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100% */ +#define PaWinMmeDontThrottleOverloadedProcessingThread (0x04) + +/* by default, the mme implementation sets the processing thread's priority to + THREAD_PRIORITY_HIGHEST. This flag sets the priority to + THREAD_PRIORITY_TIME_CRITICAL instead. Note that this has the potential + to freeze the machine, especially when used in combination with + PaWinMmeDontThrottleOverloadedProcessingThread */ +#define PaWinMmeUseTimeCriticalThreadPriority (0x05) + +typedef struct PaWinMmeDeviceAndChannelCount{ + PaDeviceIndex device; + int channelCount; +}PaWinMmeDeviceAndChannelCount; + + +typedef struct PaWinMmeStreamInfo{ + unsigned long size; /* sizeof(PaWinMmeStreamInfo) */ + PaHostApiTypeId hostApiType; /* paMME */ + unsigned long version; /* 1 */ + + unsigned long flags; + + /* low-level latency setting support + These settings control the number and size of host buffers in order + to set latency. They will be used instead of the generic parameters + to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters + flag. + */ + unsigned long framesPerBuffer; + unsigned long numBuffers; + + /* multiple devices per direction support + If flags contains the PaWinMmeUseMultipleDevices flag, + this functionality will be used, otherwise the device parameter to + Pa_OpenStream() will be used instead. + If devices are specified here, the corresponding device parameter + to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification, + otherwise an paInvalidDevice error will result. + The total number of channels accross all specified devices + must agree with the corresponding channelCount parameter to + Pa_OpenStream() otherwise a paInvalidChannelCount error will result. + */ + PaWinMmeDeviceAndChannelCount *devices; + unsigned long deviceCount; + +}PaWinMmeStreamInfo; + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PA_WIN_WMME_H */ diff --git a/pd/portaudio/pablio/pablio.h b/pd/portaudio/pablio/pablio.h index 9060c560..85843ae9 100644 --- a/pd/portaudio/pablio/pablio.h +++ b/pd/portaudio/pablio/pablio.h @@ -7,7 +7,7 @@ extern "C" #endif /* __cplusplus */ /* - * $Id: pablio.h,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * $Id: pablio.h,v 1.1.1.2 2003-05-09 16:03:59 ggeiger Exp $ * PABLIO.h * Portable Audio Blocking read/write utility. * @@ -96,8 +96,9 @@ long GetAudioStreamReadable( PABLIO_Stream *aStream ); * * flags parameter can be an ORed combination of: * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE, + * and either PABLIO_MONO or PABLIO_STEREO */ -PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, +PaError OpenAudioStream( PABLIO_Stream **aStreamPtr, double sampleRate, PaSampleFormat format, long flags ); PaError CloseAudioStream( PABLIO_Stream *aStream ); diff --git a/pd/portaudio/pablio/pablio_pd.c b/pd/portaudio/pablio/pablio_pd.c index e7105e9b..49323ef1 100644 --- a/pd/portaudio/pablio/pablio_pd.c +++ b/pd/portaudio/pablio/pablio_pd.c @@ -1,5 +1,5 @@ /* - * $Id: pablio_pd.c,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * $Id: pablio_pd.c,v 1.1.1.2 2003-05-09 16:03:59 ggeiger Exp $ * pablio.c * Portable Audio Blocking Input/Output utility. * @@ -54,7 +54,9 @@ static int blockingIOCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ); + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags myflags, + void *userData ); static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ); static PaError PABLIO_TermFIFO( RingBuffer *rbuf ); @@ -67,7 +69,9 @@ static PaError PABLIO_TermFIFO( RingBuffer *rbuf ); */ static int blockingIOCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, - PaTimestamp outTime, void *userData ) + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags myflags, + void *userData ) { PABLIO_Stream *data = (PABLIO_Stream*)userData; long numBytes = data->bytesPerFrame * framesPerBuffer; @@ -198,6 +202,7 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, long doWrite = 0; PaError err; PABLIO_Stream *aStream; + PaStreamParameters instreamparams, outstreamparams; /* MSP */ long minNumBuffers; long numFrames; @@ -208,16 +213,16 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, if (indeviceno < 0) /* MSP... */ { - indeviceno = Pa_GetDefaultInputDeviceID(); + indeviceno = Pa_GetDefaultInputDevice(); fprintf(stderr, "using default input device number: %d\n", indeviceno); } if (outdeviceno < 0) { - outdeviceno = Pa_GetDefaultOutputDeviceID(); + outdeviceno = Pa_GetDefaultOutputDevice(); fprintf(stderr, "using default output device number: %d\n", outdeviceno); } nbuffers = RoundUpToNextPowerOf2(nbuffers); - fprintf(stderr, "nchan %d, flags %d, bufs %d, framesperbuf %d\n", + fprintf(stderr, "nchan %d, flags %ld, bufs %d, framesperbuf %d\n", nchannels, flags, nbuffers, framesperbuf); /* ...MSP */ @@ -236,19 +241,22 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, aStream->samplesPerFrame = nchannels; /* MSP */ aStream->bytesPerFrame = bytesPerSample * aStream->samplesPerFrame; - /* Initialize PortAudio */ - err = Pa_Initialize(); - if( err != paNoError ) goto error; -/* Warning: numFrames must be larger than amount of data processed per - interrupt inside PA to prevent glitches. */ /* MSP */ - minNumBuffers = Pa_GetMinNumBuffers(framesperbuf, sampleRate); - if (minNumBuffers > nbuffers) - fprintf(stderr, - "warning: number of buffers %d less than recommended minimum %d\n", - (int)nbuffers, (int)minNumBuffers); numFrames = nbuffers * framesperbuf; + instreamparams.device = indeviceno; /* MSP */ + instreamparams.channelCount = nchannels; + instreamparams.sampleFormat = format; + instreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; + instreamparams.hostApiSpecificStreamInfo = 0; + + outstreamparams.device = outdeviceno; + outstreamparams.channelCount = nchannels; + outstreamparams.sampleFormat = format; + outstreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; + outstreamparams.hostApiSpecificStreamInfo = 0; + + /* Initialize Ring Buffers */ doRead = ((flags & PABLIO_READ) != 0); doWrite = ((flags & PABLIO_WRITE) != 0); @@ -271,17 +279,10 @@ PaError OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, * audio drivers. */ err = Pa_OpenStream( &aStream->stream, - (doRead ? indeviceno : paNoDevice), /* MSP */ - (doRead ? aStream->samplesPerFrame : 0 ), - format, - NULL, - (doWrite ? outdeviceno : paNoDevice), /* MSP */ - (doWrite ? aStream->samplesPerFrame : 0 ), - format, - NULL, + (doRead ? &instreamparams : 0), /* MSP */ + (doWrite ? &outstreamparams : 0), /* MSP */ sampleRate, framesperbuf, /* MSP */ - nbuffers, /* MSP */ paNoFlag, /* MSP -- portaudio will clip for us */ blockingIOCallback, aStream ); diff --git a/pd/portaudio/pablio/pablio_pd.h b/pd/portaudio/pablio/pablio_pd.h index b87b9f5a..8f8a688a 100644 --- a/pd/portaudio/pablio/pablio_pd.h +++ b/pd/portaudio/pablio/pablio_pd.h @@ -7,7 +7,7 @@ extern "C" #endif /* __cplusplus */ /* - * $Id: pablio_pd.h,v 1.1.1.1 2002-07-29 17:06:17 ggeiger Exp $ + * $Id: pablio_pd.h,v 1.1.1.2 2003-05-09 16:03:59 ggeiger Exp $ * PABLIO.h * Portable Audio Blocking read/write utility. * @@ -53,7 +53,7 @@ typedef struct { RingBuffer inFIFO; RingBuffer outFIFO; - PortAudioStream *stream; + PaStream *stream; /* MSP -- was PortAudioStream; probably an error */ int bytesPerFrame; int samplesPerFrame; } diff --git a/pd/portaudio/pablio/pablio_pd.o b/pd/portaudio/pablio/pablio_pd.o new file mode 100644 index 00000000..359a8c49 Binary files /dev/null and b/pd/portaudio/pablio/pablio_pd.o differ diff --git a/pd/portaudio/pablio/ringbuffer_pd.o b/pd/portaudio/pablio/ringbuffer_pd.o new file mode 100644 index 00000000..a348b1ed Binary files /dev/null and b/pd/portaudio/pablio/ringbuffer_pd.o differ diff --git a/pd/portaudio/testcvs/changeme.txt b/pd/portaudio/testcvs/changeme.txt new file mode 100644 index 00000000..2866c3b7 --- /dev/null +++ b/pd/portaudio/testcvs/changeme.txt @@ -0,0 +1,8 @@ +This is just a dopy little file used to test the CVS repository. +Feel free to trash this file. +Minor change. +Another tweak. +philburk tweak +stephane test +Phil changed this again on 2/21/02. Yawn... + -- cgit v1.2.1